Prepare the Workspace

knitr::opts_chunk$set(warning=FALSE, message=FALSE, error = FALSE) # rmd options
rm(list = ls()); invisible(gc()) # cleaning






Control Block

# f, f, 0.25 looks nice.
allowmigrants <- F # OPTIONS: T, F 
allowsympatry <- F # OPTIONS: T, F
minoverperc <- 0 # remove pairs that do not have thermal overlap (anagenesis)
costvar <- "ele"






Packages & Prefs

options(scipen = 999) # turn off scientific notation
'%notin%' <- Negate('%in%')
require(ggplot2) # load packages
require(GGally)
require(viridis)
require(caper)
library(dplyr)
library(stringr)
library(maps)
library(ape)
library(EnvStats)
library(forecast)
require(nlme)
require(geodist)
require(letsR)
require(spdep)
require(spatialreg)
require(rnaturalearth)
require(rnaturalearthdata)
require(rgeos)
require(sf)
require(rgdal)
require(raster)
world <- ne_coastline(scale = "medium", returnclass = "sf")
vlog <- function(x){
   log( x + abs(min( x , na.rm = T)) + 1)
}

setwd("~/Box Sync/CB_VF_Shared/Dry_Lab/Projects/JMPH/Analyze_Processed_Cluster_Outputs/Data")
The working directory was changed to /Users/boterolab1/Box Sync/CB_VF_Shared/Dry_Lab/Projects/JMPH/Analyze_Processed_Cluster_Outputs/Data inside a notebook chunk. The working directory will be reset when the chunk is finished running. Use the knitr root.dir option in the setup chunk to change the working directory for notebook chunks.
exclusion <- read.csv(file = "exclusion_nonsimpatric_nonmigrant.csv")
exclusion$realm1red[is.na(exclusion$realm1red)] <- "NA" # NA is north america not R's NA value. fix.
exclusion$realm2green[is.na(exclusion$realm2green)] <- "NA"






Load Main Data

# main dataframe ---------------------------------
setwd("~/Box Sync/CB_VF_Shared/Dry_Lab/Projects/JMPH/Process_Cluster_Outputs/Data")
load(file =  "Pair_Barrier_Data_FEB2021.rdata")
mydata <- mypairdata; rm(mypairdata)
rownames(mydata) <- mydata$Species.1
mydatahold <- mydata






initial masks

# migration ---
if(allowmigrants == F){
  mydata <- mydata[which(mydata$Migration == 1.0),]
}

# patry
if(allowsympatry == F){
  mydata <- mydata[which(mydata[,paste0(costvar, "_c0")] > 0 ),] # doesnt matter if you use ele, mat, vart paths here, will be same answer
}

sort

mydata$uniquePairId == exclusion$mydata.uniquePairId
  [1] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
 [34] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
 [67] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
[100] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
[133] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
[166] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
[199] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
[232] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
[265] FALSE FALSE FALSE FALSE
x <- mydata[which(mydata$Species.1 == "Apteryx_owenii"),]
mydata <- mydata[order(match(mydata$uniquePairId,exclusion$mydata.uniquePairId)), ]
y <- mydata[which(mydata$Species.1 == "Apteryx_owenii"),]
# sum(y!=x, na.rm = T) # checks.
# head(mydata)
mydata$uniquePairId == exclusion$mydata.uniquePairId
  [1] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE
 [41] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE
 [81] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE
[121] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE
[161] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE
[201] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE
[241] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE
rm(x,y)
# # Basic range maps for all pairs (no paths)
# wdPAM <- "/Users/boterolab1/Box Sync/CB_VF_Shared/Dry_Lab/Projects/JMPH/PREP/PAM/Data"
# setwd(wdPAM); load("cbPAM.rdata")
# setwd("~/Box Sync/CB_VF_Shared/Dry_Lab/Projects/JMPH/Process_Cluster_Outputs/Data")
# pdf("pairmaps2.pdf", width = 19, height = 9.25)
# for (i in 1:nrow(mydata)){
# x <- cbPAM[,c("Longitude(x)","Latitude(y)",mydata$Species.1bl[i])]
# x <- as.data.frame(x[x[,3] == 1,])
# if(ncol(x) == 1) {
#   x <- t(x)
#   colnames(x) <- c("lon", "lat", "pres")
#   x <- as.data.frame(x)
# } else {
#   colnames(x) <- c("lon", "lat", "pres")
# }
# 
# y <- cbPAM[,c("Longitude(x)","Latitude(y)",mydata$Species.2bl[i])]
# y <- as.data.frame(y[y[,3] == 1,])
# if(ncol(y) == 1) {
#   y <- t(y)
#   colnames(y) <- c("lon", "lat", "pres")
#   y <- as.data.frame(y)
# 
# } else {
#   colnames(y) <- c("lon", "lat", "pres")
# }
# z <- ggplot(world)+
#     geom_sf() +
#     geom_point(data = x, aes(y=lat, x=lon), color = "red") +
#     geom_point(data = y, aes(y=lat, x=lon), color = "green") +
#     theme_bw() +
#     ggtitle(i)
# print(z)
# }
# print(i)
# dev.off()
mydata_exclusion <- cbind(mydata, exclusion)
save(mydata_exclusion, file = "mydata_exclusion.rdata")
mydata$realm1 <- exclusion$realm1red
mydata$realm2 <- exclusion$realm2green
mydata$realm <- paste0(mydata$realm1, mydata$realm2)
table(mydata$realm)

AAAA AAIM ATAT ATIM ATPA IMAA IMAT IMIM IMNT NANA NTAA NTNA NTNT OCOC PAAT PANA PAPA 
  30    1   45    3    1    3    3   27    1    4    1    1  135    2    3    2    6 
mydata$realm[mydata$realm == "AAIM"]; mydata$realm[mydata$realm == "IMAA"] <-  "AAIM"
[1] "AAIM"
mydata$realm[mydata$realm == "ATIM"]; mydata$realm[mydata$realm == "IMAT"] <-  "ATIM"
[1] "ATIM" "ATIM" "ATIM"
mydata$realm[mydata$realm == "ATPA"]; mydata$realm[mydata$realm == "PAAT"] <-  "ATPA"
[1] "ATPA"
mydata$realm[mydata$realm == "IMNT"]; mydata$realm[mydata$realm == "NTIM"] <-  "IMNT"
[1] "IMNT"
mydata$realm[mydata$realm == "NTAA"]; mydata$realm[mydata$realm == "AANT"] <-  "NTAA"
[1] "NTAA"
mydata$realm[mydata$realm == "NTNA"]; mydata$realm[mydata$realm == "NANT"] <-  "NTNA"
[1] "NTNA"
mydata$realm[mydata$realm == "PANA"]; mydata$realm[mydata$realm == "NAPA"] <-  "PANA"
[1] "PANA" "PANA"
mydata$realm <- as.factor(mydata$realm); mydata$realm <- relevel(mydata$realm, "NTNT")

mydata$landgap <- as.logical(exclusion$island)

mydata$cosmopolitan <- as.logical(exclusion$cosmopolitan)

mydata$new.old <- as.logical(exclusion$new.old)

rm(exclusion, mydata_exclusion)

Impose masks & do calcuations

# filter cosmopolitan and new/old world species (there are relatively few after imposing previous masks.)
mydata <- mydata[which(mydata$cosmopolitan == FALSE & mydata$new.old == FALSE),]

# dependent variable: elevational barrier size ---
mydata$cost <- mydata[, paste0(costvar, "_c25")] 

# data filtering -----------------------
#  thermal overlap ---
mydata$MAT_overlap <- mydata[,paste0("MAT", "_ov_perc_smrnge")]
mydata <- mydata[mydata$MAT_overlap > minoverperc,]

# update sort order --------------------
mydata$sortorder <- seq(1:nrow(mydata))

# longitude ----------------------------
mydata$lon <- mydata[,paste0("lon_mean_pair_", costvar, "_c25")]

# latitude ----------------------------
mydata$lat <- mydata[,paste0("lat_mean_pair_", costvar, "_c25")]

# temperature breadth -----------------
mydata$tas_breadth <- mydata$tas_range # mean(mean(sp1 annual tas range -- one value per cell), mean(sp2 annual tas range -- one value per cell))

# mean annual temperature --------------
mydata$tas_position <- mydata$tas_mean # mean(mean(sp1 annual tas mean  -- one value per cell), mean(sp2 annual tas mean  -- one value per cell))

# precipitation breadth --------------- 
mydata$pcp_breadth <- mydata$pcp_range # mean(mean(sp1 annual pcp range  -- one value per cell), mean(sp2 annual pcp range  -- one value per cell))

# precipitation breadth --------------- 
mydata$pcp_position <- mydata$pcp_mean # mean(mean(sp1 annual pcp mean  -- one value per cell), mean(sp2 annual pcp mean  -- one value per cell))

# distance -----------------------------
mydata$distance <- mydata[,paste0("centroid_distance_",costvar,"_c25")]

# mountain mass ------------------------
mtns <- readOGR(dsn="~/Box Sync/CB_VF_Shared/Dry_Lab/Projects/JMPH/Other_Input_Data/GMBA", layer="GMBA Mountain Inventory_v1.2-World", verbose = FALSE)
wdPAM <- "/Users/boterolab1/Box Sync/CB_VF_Shared/Dry_Lab/Projects/JMPH/PREP/PAM/Data"
setwd(wdPAM); load("LonLat_BirdPAM_raster.rdata")
The working directory was changed to /Users/boterolab1/Box Sync/CB_VF_Shared/Dry_Lab/Projects/JMPH/PREP/PAM/Data inside a notebook chunk. The working directory will be reset when the chunk is finished running. Use the knitr root.dir option in the setup chunk to change the working directory for notebook chunks.
mtns <- rasterize(mtns, LonLat_BirdPAM_raster)
mtns@data@values[!is.na(mtns@data@values)] <- 1 # replace mountain IDs with simple coding. 1 for mountain...
mtns@data@values[is.na(mtns@data@values)] <- 0 # ...0 for no mountain

mydata$mtn_mass <- NA
for (i in 1:nrow(mydata)) {
  coords1<-data.frame(lon=mydata$lon[i], lat=mydata$lat[i]); coordinates(coords1)<-c("lon","lat"); crs(coords1)<-crs(LonLat_BirdPAM_raster) # get coordinates
  z <- extract(mtns, coords1, buffer = raster::pointDistance(c(0,0), c(0,8), lonlat = T))
  mydata$mtn_mass[i] <- sum(z[[1]]) / length(z[[1]])
}

load phylo and prune

setwd("~/Box Sync/CB_VF_Shared/Dry_Lab/Projects/JMPH/Other_Input_Data/BirdTrees")
The working directory was changed to /Users/boterolab1/Box Sync/CB_VF_Shared/Dry_Lab/Projects/JMPH/Other_Input_Data/BirdTrees inside a notebook chunk. The working directory will be reset when the chunk is finished running. Use the knitr root.dir option in the setup chunk to change the working directory for notebook chunks.
load(file = "BirdTrees.Rdata")

Save freame for CB

Neighbors for sptial analysis.

distm <- mydata[,c("lon", "lat")]
distm <- geodist(distm, measure = "geodesic")
rownames(distm) <- rownames(mydata)
colnames(distm) <- rownames(mydata)
basecols <- ncol(mydata)

# neightbors
coords<-cbind(mydata$lon, mydata$lat); coords<-as.matrix(coords) ; row.names(coords)<-rownames(mydata)
k1 <- knn2nb(knearneigh(coords, longlat = T))
knearneigh: identical points found
nb<- dnearneigh(coords,row.names = row.names(coords), d1=0,d2=max(unlist(nbdists(k1, coords, longlat = T))),longlat=T)

plots for transformations

for (i in c("cost", "lat", "lon","tas_breadth","tas_position","pcp_breadth","pcp_position", "mtn_mass", "dispersal_ability", "pair_age", "distance", "boundary_length", "MAT_overlap")) {
hist(mydata[, i], breaks = 50, main = i)
}

world plots

for (i in c("cost", "lat", "lon","tas_breadth","tas_position","pcp_breadth","pcp_position", "mtn_mass", "dispersal_ability", "pair_age", "distance", "boundary_length", "MAT_overlap", "realm1", "realm2", "landgap")) {
  if(i %in% c("cost")){
    myc <- mydata[, i]; myc <- vlog(myc)
    x <- ggplot(world)+
      geom_sf() +
      geom_point(data = mydata[order(mydata[, i], decreasing = F),], aes(y=lat, x=lon, color = myc), alpha = 0.75) +
      ggtitle(i)+
      scale_color_viridis()
    print(x)
  } else if (i %in% c("realm1", "realm2")){
    x <- ggplot(world)+
      geom_sf() + 
      geom_point(data = mydata[order(mydata[, "cost"], decreasing = F),], aes(y=lat, x=lon, color = get(i)), alpha = 0.75) +
      ggtitle(paste0("ln ", i))+
      scale_color_viridis(discrete = T)
    print(x) 
  } else {
    x <- ggplot(world)+
      geom_sf() + 
      geom_point(data = mydata[order(mydata[, "cost"], decreasing = F),], aes(y=lat, x=lon, color = get(i)), alpha = 0.75) +
      ggtitle(i)+
      scale_color_viridis()
    print(x) 
  }
}

mydata <- mydata[, 1:basecols]
# no spatial filtering ---------------------------------------------------------
m <- gls(scale(I(vlog(cost))) ~ scale(I(vlog(tas_breadth))), correlation = corPagel(0.99, phy = tree, fixed = F, form = ~Species.1), data = mydata, method = "REML");summary(m)
Generalized least squares fit by REML
  Model: scale(I(vlog(cost))) ~ scale(I(vlog(tas_breadth))) 
  Data: mydata 

Correlation Structure: corPagel
 Formula: ~Species.1 
 Parameter estimate(s):
    lambda 
0.02407895 

Coefficients:

 Correlation: 
                            (Intr)
scale(I(vlog(tas_breadth))) -0.013

Standardized residuals:
        Min          Q1         Med          Q3         Max 
-2.69517748 -0.72862730  0.02946462  0.64630925  2.64543853 

Residual standard error: 0.9898138 
Degrees of freedom: 235 total; 233 residual
print(paste("Correlation between data and prediction:  ", cor(predict(m),scale(I(vlog(mydata$cost))))))
[1] "Correlation between data and prediction:   0.17758346179705"
hist(resid(m))

plot(m, resid(., type = "p") ~ fitted(.), abline = 0)

plot(m, scale(I(vlog(cost))) ~ fitted(.), abline = c(0,1))

qqnorm(m)


# plot ---------------------------------
plot(scale(I(vlog(mydata$tas_breadth))), scale(I(vlog(mydata$cost))), pch = 16,
     xlab = "thermal niche breadth", ylab = paste0("log ", costvar, " cost" ), main = "Global model")
myx <- seq(min(scale(vlog(mydata$tas_breadth))), max(scale(vlog(mydata$tas_breadth))), length.out=100) # plot fit
mycoefs<-coef(m)
myy <- mycoefs[1] + mycoefs[2]*myx
lines(c(0,0), c(-10,100), lty=2)
lines(myx,myy,col = 'red')
m <- gls(scale(I(vlog(cost))) ~ scale(I(log(tas_breadth))), data = mydata, method = "REML")
myx <- seq(min(scale(vlog(mydata$tas_breadth))), max(scale(vlog(mydata$tas_breadth))), length.out=100) # plot fit
mycoefs<-coef(m)
myy <- mycoefs[1] + mycoefs[2]*myx
lines(c(0,0), c(-10,100), lty=2)
lines(myx,myy,col = 'red', lty=2)





# with spatial filtering -------------------------------------------------------
m <- gls(scale(I(vlog(cost))) ~ scale(I(vlog(tas_breadth))), correlation = corPagel(0.99, phy = tree, fixed = F, form = ~Species.1), data = mydata, method = "REML")
# spatial autocorr ---------------------
matx <- as.matrix(m$residuals); rownames(matx) <- rownames(mydata)
spac <- lets.correl(x=matx, y=distm, z=12, equidistant = T, plot = T)

moran.test(residuals(m), nb2listw(nb))$p.value # no evidence of spatial component.. 
[1] 0.00000001756702
# spatial filtering --------------------
rm(sarcol)
object 'sarcol' not found
sarcol <- SpatialFiltering(formula = scale(I(vlog(cost))) ~ scale(I(vlog(tas_breadth))),
                           data = mydata,nb=nb, style="W", ExactEV = TRUE)
mydata[,c((basecols+1):(basecols+1+ dim(fitted(sarcol))[2]-1))]<-fitted(sarcol)
colnames(mydata) # 4 vectors created.
 [1] "uniquePairId"      "Species.1"         "Species.2"         "Species.1bl"       "Species.2bl"       "cost"              "lat"               "lon"               "tas_breadth"      
[10] "tas_position"      "pcp_breadth"       "pcp_position"      "mtn_mass"          "water_buffering"   "dispersal_ability" "pair_age"          "distance"          "boundary_length"  
[19] "MAT_overlap"       "realm1"            "realm2"            "landgap"           "V23"               "V24"               "V25"               "V26"               "V27"              
[28] "V28"               "V29"               "V30"               "V31"              
# vector 1 added ---
m <- gls(scale(I(vlog(cost))) ~ scale(I(vlog(tas_breadth))) + V23 + V24 + V25 + V26 + V27 + V28 + V29 + V30 + V31, correlation = corPagel(0.99, phy = tree,fixed = F, form = ~Species.1), data = mydata, method = "REML"); summary(m); cor(predict(m),scale(I(vlog(mydata$cost))))
Generalized least squares fit by REML
  Model: scale(I(vlog(cost))) ~ scale(I(vlog(tas_breadth))) + V23 + V24 +      V25 + V26 + V27 + V28 + V29 + V30 + V31 
  Data: mydata 

Correlation Structure: corPagel
 Formula: ~Species.1 
 Parameter estimate(s):
   lambda 
0.1132853 

Coefficients:

 Correlation: 
                            (Intr) s(I((_ V23    V24    V25    V26    V27    V28    V29    V30   
scale(I(vlog(tas_breadth))) -0.030                                                               
V23                          0.060 -0.016                                                        
V24                         -0.033  0.002 -0.008                                                 
V25                         -0.031  0.016 -0.012  0.005                                          
V26                          0.015  0.020 -0.003 -0.014  0.004                                   
V27                          0.032  0.007  0.004 -0.015 -0.006  0.006                            
V28                         -0.018 -0.020  0.003  0.003  0.001 -0.010 -0.003                     
V29                          0.068  0.000  0.001  0.000  0.002  0.003 -0.017  0.000              
V30                         -0.038  0.001 -0.012  0.002  0.017 -0.001 -0.018  0.001  0.000       
V31                          0.031  0.024 -0.007 -0.013 -0.011  0.013  0.061 -0.015  0.001 -0.014

Standardized residuals:
       Min         Q1        Med         Q3        Max 
-3.0866866 -0.5483974  0.1080253  0.6419830  2.6354891 

Residual standard error: 0.8999846 
Degrees of freedom: 235 total; 224 residual
          [,1]
[1,] 0.5013446
matx <- as.matrix(m$residuals); rownames(matx) <- rownames(mydata)
spac <- lets.correl(x=matx, y=distm, z=12, equidistant = T, plot = T)

moran.test(residuals(m), nb2listw(nb))$p.value # too much.
[1] 0.9215695
hist(resid(m))

plot(m, resid(., type = "p") ~ fitted(.), abline = 0)

plot(m, scale(I(vlog(cost))) ~ fitted(.), abline = c(0,1))

qqnorm(m)


# for(i in c("V23", "V24", "V25", "V26", "V27", "V28", "V29", "V30", "V31")){
#   x <- ggplot(world)+
#   geom_sf() +
#   geom_point(data = mydata[order(mydata[,i]),], aes(y=lat, x=lon, color = mydata[,i]), alpha = 0.9) +
#   scale_color_viridis()
#   print(x)
# }

Is thermal niche breadth predicted by latitude plus latitude2?

mydata <- mydata[, 1:basecols]
m <- gls(scale(I(vlog(tas_breadth))) ~ scale(lat) + scale(I(lat^2)), correlation = corPagel(0.99, phy = tree, fixed = F, form = ~Species.1), data = mydata, method = "REML"); summary(m)
Generalized least squares fit by REML
  Model: scale(I(vlog(tas_breadth))) ~ scale(lat) + scale(I(lat^2)) 
  Data: mydata 

Correlation Structure: corPagel
 Formula: ~Species.1 
 Parameter estimate(s):
   lambda 
0.3441942 

Coefficients:

 Correlation: 
                (Intr) scl(l)
scale(lat)       0.026       
scale(I(lat^2)) -0.078  0.085

Standardized residuals:
       Min         Q1        Med         Q3        Max 
-1.8535474 -0.6164704  0.1168708  0.7632055  3.0128361 

Residual standard error: 0.7782013 
Degrees of freedom: 235 total; 232 residual
print(paste("Correlation between data and prediction:  ", cor(predict(m),scale(I(vlog(mydata$tas_breadth))))))
[1] "Correlation between data and prediction:   0.700677229106234"
hist(resid(m))

plot(m, resid(., type = "p") ~ fitted(.), abline = 0)

plot(m, scale(I(vlog(cost))) ~ fitted(.), abline = c(0,1))

qqnorm(m)


# plot ---------------------------------
plot(scale(mydata$lat), scale(I(vlog(mydata$tas_breadth))), pch = 16,
     xlab = "latitude", ylab = "thermal niche breadth", main = "Global model")
myx <- seq(min(scale(mydata$lat)), max(scale(mydata$lat)), length.out=100) # plot fit
mycoefs<-coef(m)
myy <- mycoefs[1] + mycoefs[2]*myx + mycoefs[3]*myx^2
lines(c(0,0), c(-10,100), lty=2)
lines(myx,myy,col = 'red')
m <- gls(scale(I(vlog(tas_breadth))) ~ scale(lat) + scale(I(lat^2)), data = mydata, method = "REML")
myx <- seq(min(scale(mydata$lat)), max(scale(mydata$lat)), length.out=100) # plot fit
mycoefs<-coef(m)
myy <- mycoefs[1] + mycoefs[2]*myx + mycoefs[3]*myx^2
lines(c(0,0), c(-10,100), lty=2)
lines(myx,myy,col = 'red', lty=2)







# with spatial filtering -------------------------------------------------------
m <- gls(scale(I(vlog(tas_breadth))) ~ scale(lat) + scale(I(lat^2)), correlation = corPagel(0.99, phy = tree, fixed = F, form = ~Species.1), data = mydata, method = "REML")
# spatial autocorr ---------------------
matx <- as.matrix(m$residuals); rownames(matx) <- rownames(mydata)
spac <- lets.correl(x=matx, y=distm, z=12, equidistant = T, plot = T)

moran.test(residuals(m), nb2listw(nb))$p.value # no evidence of spatial component.. 
[1] 0.000000000000000000000000000000000000000000000000000000000000000009068726
# spatial filtering --------------------
rm(sarcol)
sarcol <- SpatialFiltering(formula = scale(I(vlog(tas_breadth))) ~ scale(lat) + scale(I(lat^2)),
                           data = mydata,nb=nb, style="W", ExactEV = TRUE)
mydata[,c((basecols+1):(basecols+1+ dim(fitted(sarcol))[2]-1))]<-fitted(sarcol)
colnames(mydata) # 4 vectors created.
 [1] "uniquePairId"      "Species.1"         "Species.2"         "Species.1bl"       "Species.2bl"       "cost"              "lat"               "lon"               "tas_breadth"      
[10] "tas_position"      "pcp_breadth"       "pcp_position"      "mtn_mass"          "water_buffering"   "dispersal_ability" "pair_age"          "distance"          "boundary_length"  
[19] "MAT_overlap"       "realm1"            "realm2"            "landgap"           "V23"               "V24"               "V25"               "V26"               "V27"              
[28] "V28"               "V29"               "V30"               "V31"               "V32"               "V33"               "V34"               "V35"               "V36"              
[37] "V37"               "V38"              
# vector 1 added ---
m <- gls(scale(I(vlog(tas_breadth))) ~ scale(lat) + scale(I(lat^2)) + V23 + V24 + V25 + V26 + V27 + V28 + V29 + V30 + V31 + V32 + V33 + V34 + V35 + V36 + V37 + V38, correlation = corPagel(0.99, phy = tree,fixed = F, form = ~Species.1), data = mydata, method = "REML"); summary(m); cor(predict(m),scale(I(vlog(mydata$cost))))
Generalized least squares fit by REML
  Model: scale(I(vlog(tas_breadth))) ~ scale(lat) + scale(I(lat^2)) +      V23 + V24 + V25 + V26 + V27 + V28 + V29 + V30 + V31 + V32 +      V33 + V34 + V35 + V36 + V37 + V38 
  Data: mydata 

Correlation Structure: corPagel
 Formula: ~Species.1 
 Parameter estimate(s):
   lambda 
0.5675592 

Coefficients:

 Correlation: 
                (Intr) scl(l) s(I(^2 V23    V24    V25    V26    V27    V28    V29    V30    V31    V32    V33    V34    V35    V36    V37   
scale(lat)       0.045                                                                                                                       
scale(I(lat^2)) -0.077  0.055                                                                                                                
V23              0.011 -0.056 -0.055                                                                                                         
V24              0.070  0.021  0.019  0.048                                                                                                  
V25              0.053  0.032 -0.058  0.076  0.005                                                                                           
V26             -0.024  0.005 -0.134  0.208  0.006  0.167                                                                                    
V27             -0.072 -0.037  0.034  0.040 -0.044 -0.007 -0.014                                                                             
V28             -0.007 -0.004 -0.078  0.050 -0.032 -0.022  0.090  0.028                                                                      
V29              0.028 -0.005 -0.006 -0.069 -0.003  0.037 -0.018 -0.057 -0.047                                                               
V30              0.002  0.011 -0.022 -0.006  0.002 -0.052  0.005  0.011  0.048 -0.004                                                        
V31             -0.038 -0.018 -0.018 -0.017 -0.009 -0.013 -0.005  0.030 -0.011 -0.008 -0.002                                                 
V32              0.005 -0.030 -0.009 -0.062 -0.030  0.021 -0.006 -0.046 -0.019  0.014 -0.023 -0.004                                          
V33              0.001 -0.005  0.012  0.022  0.004 -0.002 -0.021  0.004 -0.003 -0.002 -0.008 -0.011 -0.037                                   
V34             -0.008 -0.016 -0.070  0.043 -0.012 -0.014  0.026  0.012  0.050 -0.015  0.014  0.003 -0.007 -0.003                            
V35             -0.021  0.062  0.018 -0.018  0.010 -0.030 -0.030  0.026  0.022 -0.022  0.074  0.012 -0.027  0.010  0.036                     
V36             -0.005  0.012 -0.007 -0.006 -0.010 -0.013 -0.012 -0.001 -0.007 -0.001 -0.001  0.001 -0.020  0.002  0.003  0.004              
V37             -0.045 -0.014 -0.034  0.063 -0.007 -0.005  0.055  0.025  0.041 -0.022  0.021  0.008 -0.073 -0.028  0.013 -0.004 -0.003       
V38              0.027 -0.017 -0.021  0.004  0.017  0.013 -0.006  0.010 -0.006 -0.003 -0.015  0.002  0.008 -0.014 -0.005 -0.012  0.001 -0.008

Standardized residuals:
       Min         Q1        Med         Q3        Max 
-2.0731629 -0.6096382 -0.1790288  0.2623287  3.2865813 

Residual standard error: 0.5443762 
Degrees of freedom: 235 total; 216 residual
          [,1]
[1,] 0.1123255
matx <- as.matrix(m$residuals); rownames(matx) <- rownames(mydata)
spac <- lets.correl(x=matx, y=distm, z=12, equidistant = T, plot = T)

moran.test(residuals(m), nb2listw(nb))$p.value # too much.
[1] 0.9484634
hist(resid(m))

plot(m, resid(., type = "p") ~ fitted(.), abline = 0)

plot(m, scale(I(vlog(cost))) ~ fitted(.), abline = c(0,1))

qqnorm(m)


# 
# for(i in c("V23", "V24", "V25", "V26", "V27", "V28", "V29", "V30", "V31", "V32", "V33", "V34", "V35", "V36", "V37", "V38")){
#   x <- ggplot(world)+
#   geom_sf() +
#   geom_point(data = mydata[order(mydata[,i]),], aes(y=lat, x=lon, color = mydata[,i]), alpha = 0.9) +
#   scale_color_viridis()
#   print(x)
# }

Is thermal niche breadth predicted by latitude plus latitude2?

mydata <- mydata[, 1:basecols]
m <- gls(scale(I(vlog(cost))) ~ scale(lat) + scale(I(lat^2)), correlation = corPagel(0.99, phy = tree, fixed = F, form = ~Species.1), data = mydata, method = "REML"); summary(m)
Generalized least squares fit by REML
  Model: scale(I(vlog(cost))) ~ scale(lat) + scale(I(lat^2)) 
  Data: mydata 

Correlation Structure: corPagel
 Formula: ~Species.1 
 Parameter estimate(s):
     lambda 
-0.01153228 

Coefficients:

 Correlation: 
                (Intr) scl(l)
scale(lat)      -0.016       
scale(I(lat^2))  0.118  0.086

Standardized residuals:
        Min          Q1         Med          Q3         Max 
-3.09043113 -0.70872513  0.06485171  0.61849430  2.67235129 

Residual standard error: 0.9785129 
Degrees of freedom: 235 total; 232 residual
print(paste("Correlation between data and prediction:  ", cor(predict(m),scale(I(vlog(mydata$cost))))))
[1] "Correlation between data and prediction:   0.218100113560087"
hist(resid(m))

plot(m, resid(., type = "p") ~ fitted(.), abline = 0)

plot(m, scale(I(vlog(cost))) ~ fitted(.), abline = c(0,1))

qqnorm(m)


# plot ---------------------------------
plot(scale(mydata$lat), scale(I(vlog(mydata$cost))), pch = 16,
     xlab = "latitude", ylab = "thermal niche breadth", main = "Global model")
myx <- seq(min(scale(mydata$lat)), max(scale(mydata$lat)), length.out=100) # plot fit
mycoefs<-coef(m)
myy <- mycoefs[1] + mycoefs[2]*myx + mycoefs[3]*myx^2
lines(c(0,0), c(-10,100), lty=2)
lines(myx,myy,col = 'red')
m <- gls(scale(I(vlog(cost))) ~ scale(lat) + scale(I(lat^2)), data = mydata, method = "REML")
myx <- seq(min(scale(mydata$lat)), max(scale(mydata$lat)), length.out=100) # plot fit
mycoefs<-coef(m)
myy <- mycoefs[1] + mycoefs[2]*myx + mycoefs[3]*myx^2
lines(c(0,0), c(-10,100), lty=2)
lines(myx,myy,col = 'red', lty=2)







# with spatial filtering -------------------------------------------------------
m <- gls(scale(I(vlog(cost))) ~ scale(lat) + scale(I(lat^2)), correlation = corPagel(0.99, phy = tree, fixed = F, form = ~Species.1), data = mydata, method = "REML")
# spatial autocorr ---------------------
matx <- as.matrix(m$residuals); rownames(matx) <- rownames(mydata)
spac <- lets.correl(x=matx, y=distm, z=12, equidistant = T, plot = T)

moran.test(residuals(m), nb2listw(nb))$p.value # no evidence of spatial component.. 
[1] 0.0001422316
# spatial filtering --------------------
rm(sarcol)
sarcol <- SpatialFiltering(formula = scale(I(vlog(cost))) ~ scale(lat) + scale(I(lat^2)),
                           data = mydata,nb=nb, style="W", ExactEV = TRUE)
mydata[,c((basecols+1):(basecols+1+ dim(fitted(sarcol))[2]-1))]<-fitted(sarcol)
colnames(mydata) # 4 vectors created.
 [1] "uniquePairId"      "Species.1"         "Species.2"         "Species.1bl"       "Species.2bl"       "cost"              "lat"               "lon"               "tas_breadth"       "tas_position"      "pcp_breadth"       "pcp_position"      "mtn_mass"         
[14] "water_buffering"   "dispersal_ability" "pair_age"          "distance"          "boundary_length"   "MAT_overlap"       "realm1"            "realm2"            "landgap"           "V23"               "V24"               "V25"               "V26"              
# vector 1 added ---
m <- gls(scale(I(vlog(cost))) ~ scale(lat) + scale(I(lat^2)) + V23 + V24 + V25 + V26, correlation = corPagel(0.99, phy = tree,fixed = F, form = ~Species.1), data = mydata, method = "REML"); summary(m); cor(predict(m),scale(I(vlog(mydata$cost))))
Generalized least squares fit by REML
  Model: scale(I(vlog(cost))) ~ scale(lat) + scale(I(lat^2)) + V23 + V24 +      V25 + V26 
  Data: mydata 

Correlation Structure: corPagel
 Formula: ~Species.1 
 Parameter estimate(s):
   lambda 
0.1048059 

Coefficients:

 Correlation: 
                (Intr) scl(l) s(I(^2 V23    V24    V25   
scale(lat)       0.001                                   
scale(I(lat^2)) -0.064  0.104                            
V23             -0.070 -0.017  0.013                     
V24             -0.041 -0.007 -0.006  0.011              
V25             -0.031  0.013  0.018  0.009  0.001       
V26              0.032  0.005 -0.013 -0.021 -0.008 -0.004

Standardized residuals:
       Min         Q1        Med         Q3        Max 
-3.2135868 -0.5833627  0.1685737  0.6876315  2.6281318 

Residual standard error: 0.9181122 
Degrees of freedom: 235 total; 228 residual
          [,1]
[1,] 0.4518531
matx <- as.matrix(m$residuals); rownames(matx) <- rownames(mydata)
spac <- lets.correl(x=matx, y=distm, z=12, equidistant = T, plot = T)

moran.test(residuals(m), nb2listw(nb))$p.value # too much.
[1] 0.7738208
hist(resid(m))

plot(m, resid(., type = "p") ~ fitted(.), abline = 0)

plot(m, scale(I(vlog(cost))) ~ fitted(.), abline = c(0,1))

qqnorm(m)


for(i in c("V23", "V24", "V25", "V26")){
  x <- ggplot(world)+
  geom_sf() +
  geom_point(data = mydata[order(mydata[,i]),], aes(y=lat, x=lon, color = mydata[,i]), alpha = 0.9) +
  scale_color_viridis()
  print(x)
}

print(paste("Correlation between data and prediction:  ", cor(predict(m),scale(I(vlog(mydata$cost))))))
[1] "Correlation between data and prediction:   0.694878434079338"

Sensitivity Analyses

# 1 Pair age (all v. < 8mya (end of uplift of Andes))
# 2 Distance (all v. < 1500*1000) (1500 / 110 = ~ 22 degrees)
# 3 MAT_overlap (> 0% v. > 75% (more restrictive == more conservative for this measure.))
# 4 landgap (all v. nogap) *ALL GAPS ARE < 110km (two water grid cells marked as land for having >50% land @ 0.5 degree resolution.)
LS0tCnRpdGxlOiAiUiBOb3RlYm9vayIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKClByZXBhcmUgdGhlIFdvcmtzcGFjZQpgYGB7cn0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIGVycm9yID0gRkFMU0UpICMgcm1kIG9wdGlvbnMKcm0obGlzdCA9IGxzKCkpOyBpbnZpc2libGUoZ2MoKSkgIyBjbGVhbmluZwpgYGAKPGJyPjxicj48YnI+PGJyPjxicj4KCkNvbnRyb2wgQmxvY2sKYGBge3J9IAojIGYsIGYsIDAuMjUgbG9va3MgbmljZS4KYWxsb3dtaWdyYW50cyA8LSBGICMgT1BUSU9OUzogVCwgRiAKYWxsb3dzeW1wYXRyeSA8LSBGICMgT1BUSU9OUzogVCwgRgptaW5vdmVycGVyYyA8LSAwICMgcmVtb3ZlIHBhaXJzIHRoYXQgZG8gbm90IGhhdmUgdGhlcm1hbCBvdmVybGFwIChhbmFnZW5lc2lzKQpjb3N0dmFyIDwtICJlbGUiCmBgYAo8YnI+PGJyPjxicj48YnI+PGJyPgoKUGFja2FnZXMgJiBQcmVmcwpgYGB7cn0Kb3B0aW9ucyhzY2lwZW4gPSA5OTkpICMgdHVybiBvZmYgc2NpZW50aWZpYyBub3RhdGlvbgonJW5vdGluJScgPC0gTmVnYXRlKCclaW4lJykKcmVxdWlyZShnZ3Bsb3QyKSAjIGxvYWQgcGFja2FnZXMKcmVxdWlyZShHR2FsbHkpCnJlcXVpcmUodmlyaWRpcykKcmVxdWlyZShjYXBlcikKbGlicmFyeShkcGx5cikKbGlicmFyeShzdHJpbmdyKQpsaWJyYXJ5KG1hcHMpCmxpYnJhcnkoYXBlKQpsaWJyYXJ5KEVudlN0YXRzKQpsaWJyYXJ5KGZvcmVjYXN0KQpyZXF1aXJlKG5sbWUpCnJlcXVpcmUoZ2VvZGlzdCkKcmVxdWlyZShsZXRzUikKcmVxdWlyZShzcGRlcCkKcmVxdWlyZShzcGF0aWFscmVnKQpyZXF1aXJlKHJuYXR1cmFsZWFydGgpCnJlcXVpcmUocm5hdHVyYWxlYXJ0aGRhdGEpCnJlcXVpcmUocmdlb3MpCnJlcXVpcmUoc2YpCnJlcXVpcmUocmdkYWwpCnJlcXVpcmUocmFzdGVyKQp3b3JsZCA8LSBuZV9jb2FzdGxpbmUoc2NhbGUgPSAibWVkaXVtIiwgcmV0dXJuY2xhc3MgPSAic2YiKQp2bG9nIDwtIGZ1bmN0aW9uKHgpewogICBsb2coIHggKyBhYnMobWluKCB4ICwgbmEucm0gPSBUKSkgKyAxKQp9CgpzZXR3ZCgifi9Cb3ggU3luYy9DQl9WRl9TaGFyZWQvRHJ5X0xhYi9Qcm9qZWN0cy9KTVBIL0FuYWx5emVfUHJvY2Vzc2VkX0NsdXN0ZXJfT3V0cHV0cy9EYXRhIikKZXhjbHVzaW9uIDwtIHJlYWQuY3N2KGZpbGUgPSAiZXhjbHVzaW9uX25vbnNpbXBhdHJpY19ub25taWdyYW50LmNzdiIpCmV4Y2x1c2lvbiRyZWFsbTFyZWRbaXMubmEoZXhjbHVzaW9uJHJlYWxtMXJlZCldIDwtICJOQSIgIyBOQSBpcyBub3J0aCBhbWVyaWNhIG5vdCBSJ3MgTkEgdmFsdWUuIGZpeC4KZXhjbHVzaW9uJHJlYWxtMmdyZWVuW2lzLm5hKGV4Y2x1c2lvbiRyZWFsbTJncmVlbildIDwtICJOQSIKYGBgCjxicj48YnI+PGJyPjxicj48YnI+CgpMb2FkIE1haW4gRGF0YQpgYGB7cn0KIyBtYWluIGRhdGFmcmFtZSAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0Kc2V0d2QoIn4vQm94IFN5bmMvQ0JfVkZfU2hhcmVkL0RyeV9MYWIvUHJvamVjdHMvSk1QSC9Qcm9jZXNzX0NsdXN0ZXJfT3V0cHV0cy9EYXRhIikKbG9hZChmaWxlID0gICJQYWlyX0JhcnJpZXJfRGF0YV9GRUIyMDIxLnJkYXRhIikKbXlkYXRhIDwtIG15cGFpcmRhdGE7IHJtKG15cGFpcmRhdGEpCnJvd25hbWVzKG15ZGF0YSkgPC0gbXlkYXRhJFNwZWNpZXMuMQpteWRhdGFob2xkIDwtIG15ZGF0YQpgYGAKPGJyPjxicj48YnI+PGJyPjxicj4KCmluaXRpYWwgbWFza3MKYGBge3J9CiMgbWlncmF0aW9uIC0tLQppZihhbGxvd21pZ3JhbnRzID09IEYpewogIG15ZGF0YSA8LSBteWRhdGFbd2hpY2gobXlkYXRhJE1pZ3JhdGlvbiA9PSAxLjApLF0KfQoKIyBwYXRyeQppZihhbGxvd3N5bXBhdHJ5ID09IEYpewogIG15ZGF0YSA8LSBteWRhdGFbd2hpY2gobXlkYXRhWyxwYXN0ZTAoY29zdHZhciwgIl9jMCIpXSA+IDAgKSxdICMgZG9lc250IG1hdHRlciBpZiB5b3UgdXNlIGVsZSwgbWF0LCB2YXJ0IHBhdGhzIGhlcmUsIHdpbGwgYmUgc2FtZSBhbnN3ZXIKfQpgYGAKCnNvcnQKYGBge3J9Cm15ZGF0YSR1bmlxdWVQYWlySWQgPT0gZXhjbHVzaW9uJG15ZGF0YS51bmlxdWVQYWlySWQKeCA8LSBteWRhdGFbd2hpY2gobXlkYXRhJFNwZWNpZXMuMSA9PSAiQXB0ZXJ5eF9vd2VuaWkiKSxdCm15ZGF0YSA8LSBteWRhdGFbb3JkZXIobWF0Y2gobXlkYXRhJHVuaXF1ZVBhaXJJZCxleGNsdXNpb24kbXlkYXRhLnVuaXF1ZVBhaXJJZCkpLCBdCnkgPC0gbXlkYXRhW3doaWNoKG15ZGF0YSRTcGVjaWVzLjEgPT0gIkFwdGVyeXhfb3dlbmlpIiksXQojIHN1bSh5IT14LCBuYS5ybSA9IFQpICMgY2hlY2tzLgojIGhlYWQobXlkYXRhKQpteWRhdGEkdW5pcXVlUGFpcklkID09IGV4Y2x1c2lvbiRteWRhdGEudW5pcXVlUGFpcklkCnJtKHgseSkKYGBgCgoKCmBgYHtyfQojICMgQmFzaWMgcmFuZ2UgbWFwcyBmb3IgYWxsIHBhaXJzIChubyBwYXRocykKIyB3ZFBBTSA8LSAiL1VzZXJzL2JvdGVyb2xhYjEvQm94IFN5bmMvQ0JfVkZfU2hhcmVkL0RyeV9MYWIvUHJvamVjdHMvSk1QSC9QUkVQL1BBTS9EYXRhIgojIHNldHdkKHdkUEFNKTsgbG9hZCgiY2JQQU0ucmRhdGEiKQojIHNldHdkKCJ+L0JveCBTeW5jL0NCX1ZGX1NoYXJlZC9EcnlfTGFiL1Byb2plY3RzL0pNUEgvUHJvY2Vzc19DbHVzdGVyX091dHB1dHMvRGF0YSIpCiMgcGRmKCJwYWlybWFwczIucGRmIiwgd2lkdGggPSAxOSwgaGVpZ2h0ID0gOS4yNSkKIyBmb3IgKGkgaW4gMTpucm93KG15ZGF0YSkpewojIHggPC0gY2JQQU1bLGMoIkxvbmdpdHVkZSh4KSIsIkxhdGl0dWRlKHkpIixteWRhdGEkU3BlY2llcy4xYmxbaV0pXQojIHggPC0gYXMuZGF0YS5mcmFtZSh4W3hbLDNdID09IDEsXSkKIyBpZihuY29sKHgpID09IDEpIHsKIyAgIHggPC0gdCh4KQojICAgY29sbmFtZXMoeCkgPC0gYygibG9uIiwgImxhdCIsICJwcmVzIikKIyAgIHggPC0gYXMuZGF0YS5mcmFtZSh4KQojIH0gZWxzZSB7CiMgICBjb2xuYW1lcyh4KSA8LSBjKCJsb24iLCAibGF0IiwgInByZXMiKQojIH0KIyAKIyB5IDwtIGNiUEFNWyxjKCJMb25naXR1ZGUoeCkiLCJMYXRpdHVkZSh5KSIsbXlkYXRhJFNwZWNpZXMuMmJsW2ldKV0KIyB5IDwtIGFzLmRhdGEuZnJhbWUoeVt5WywzXSA9PSAxLF0pCiMgaWYobmNvbCh5KSA9PSAxKSB7CiMgICB5IDwtIHQoeSkKIyAgIGNvbG5hbWVzKHkpIDwtIGMoImxvbiIsICJsYXQiLCAicHJlcyIpCiMgICB5IDwtIGFzLmRhdGEuZnJhbWUoeSkKIyAKIyB9IGVsc2UgewojICAgY29sbmFtZXMoeSkgPC0gYygibG9uIiwgImxhdCIsICJwcmVzIikKIyB9CiMgeiA8LSBnZ3Bsb3Qod29ybGQpKwojICAgICBnZW9tX3NmKCkgKwojICAgICBnZW9tX3BvaW50KGRhdGEgPSB4LCBhZXMoeT1sYXQsIHg9bG9uKSwgY29sb3IgPSAicmVkIikgKwojICAgICBnZW9tX3BvaW50KGRhdGEgPSB5LCBhZXMoeT1sYXQsIHg9bG9uKSwgY29sb3IgPSAiZ3JlZW4iKSArCiMgICAgIHRoZW1lX2J3KCkgKwojICAgICBnZ3RpdGxlKGkpCiMgcHJpbnQoeikKIyB9CiMgcHJpbnQoaSkKIyBkZXYub2ZmKCkKYGBgCgpgYGB7cn0KbXlkYXRhX2V4Y2x1c2lvbiA8LSBjYmluZChteWRhdGEsIGV4Y2x1c2lvbikKc2F2ZShteWRhdGFfZXhjbHVzaW9uLCBmaWxlID0gIm15ZGF0YV9leGNsdXNpb24ucmRhdGEiKQpteWRhdGEkcmVhbG0xIDwtIGV4Y2x1c2lvbiRyZWFsbTFyZWQKbXlkYXRhJHJlYWxtMiA8LSBleGNsdXNpb24kcmVhbG0yZ3JlZW4KbXlkYXRhJHJlYWxtIDwtIHBhc3RlMChteWRhdGEkcmVhbG0xLCBteWRhdGEkcmVhbG0yKQp0YWJsZShteWRhdGEkcmVhbG0pCm15ZGF0YSRyZWFsbVtteWRhdGEkcmVhbG0gPT0gIkFBSU0iXTsgbXlkYXRhJHJlYWxtW215ZGF0YSRyZWFsbSA9PSAiSU1BQSJdIDwtICAiQUFJTSIKbXlkYXRhJHJlYWxtW215ZGF0YSRyZWFsbSA9PSAiQVRJTSJdOyBteWRhdGEkcmVhbG1bbXlkYXRhJHJlYWxtID09ICJJTUFUIl0gPC0gICJBVElNIgpteWRhdGEkcmVhbG1bbXlkYXRhJHJlYWxtID09ICJBVFBBIl07IG15ZGF0YSRyZWFsbVtteWRhdGEkcmVhbG0gPT0gIlBBQVQiXSA8LSAgIkFUUEEiCm15ZGF0YSRyZWFsbVtteWRhdGEkcmVhbG0gPT0gIklNTlQiXTsgbXlkYXRhJHJlYWxtW215ZGF0YSRyZWFsbSA9PSAiTlRJTSJdIDwtICAiSU1OVCIKbXlkYXRhJHJlYWxtW215ZGF0YSRyZWFsbSA9PSAiTlRBQSJdOyBteWRhdGEkcmVhbG1bbXlkYXRhJHJlYWxtID09ICJBQU5UIl0gPC0gICJOVEFBIgpteWRhdGEkcmVhbG1bbXlkYXRhJHJlYWxtID09ICJOVE5BIl07IG15ZGF0YSRyZWFsbVtteWRhdGEkcmVhbG0gPT0gIk5BTlQiXSA8LSAgIk5UTkEiCm15ZGF0YSRyZWFsbVtteWRhdGEkcmVhbG0gPT0gIlBBTkEiXTsgbXlkYXRhJHJlYWxtW215ZGF0YSRyZWFsbSA9PSAiTkFQQSJdIDwtICAiUEFOQSIKbXlkYXRhJHJlYWxtIDwtIGFzLmZhY3RvcihteWRhdGEkcmVhbG0pOyBteWRhdGEkcmVhbG0gPC0gcmVsZXZlbChteWRhdGEkcmVhbG0sICJOVE5UIikKCm15ZGF0YSRsYW5kZ2FwIDwtIGFzLmxvZ2ljYWwoZXhjbHVzaW9uJGlzbGFuZCkKCm15ZGF0YSRjb3Ntb3BvbGl0YW4gPC0gYXMubG9naWNhbChleGNsdXNpb24kY29zbW9wb2xpdGFuKQoKbXlkYXRhJG5ldy5vbGQgPC0gYXMubG9naWNhbChleGNsdXNpb24kbmV3Lm9sZCkKCnJtKGV4Y2x1c2lvbiwgbXlkYXRhX2V4Y2x1c2lvbikKYGBgCgoKCkltcG9zZSBtYXNrcyAmIGRvIGNhbGN1YXRpb25zCmBgYHtyfQojIGZpbHRlciBjb3Ntb3BvbGl0YW4gYW5kIG5ldy9vbGQgd29ybGQgc3BlY2llcyAodGhlcmUgYXJlIHJlbGF0aXZlbHkgZmV3IGFmdGVyIGltcG9zaW5nIHByZXZpb3VzIG1hc2tzLikKbXlkYXRhIDwtIG15ZGF0YVt3aGljaChteWRhdGEkY29zbW9wb2xpdGFuID09IEZBTFNFICYgbXlkYXRhJG5ldy5vbGQgPT0gRkFMU0UpLF0KCiMgZGVwZW5kZW50IHZhcmlhYmxlOiBlbGV2YXRpb25hbCBiYXJyaWVyIHNpemUgLS0tCm15ZGF0YSRjb3N0IDwtIG15ZGF0YVssIHBhc3RlMChjb3N0dmFyLCAiX2MyNSIpXSAKCiMgZGF0YSBmaWx0ZXJpbmcgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyAgdGhlcm1hbCBvdmVybGFwIC0tLQpteWRhdGEkTUFUX292ZXJsYXAgPC0gbXlkYXRhWyxwYXN0ZTAoIk1BVCIsICJfb3ZfcGVyY19zbXJuZ2UiKV0KbXlkYXRhIDwtIG15ZGF0YVtteWRhdGEkTUFUX292ZXJsYXAgPiBtaW5vdmVycGVyYyxdCgojIHVwZGF0ZSBzb3J0IG9yZGVyIC0tLS0tLS0tLS0tLS0tLS0tLS0tCm15ZGF0YSRzb3J0b3JkZXIgPC0gc2VxKDE6bnJvdyhteWRhdGEpKQoKIyBsb25naXR1ZGUgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQpteWRhdGEkbG9uIDwtIG15ZGF0YVsscGFzdGUwKCJsb25fbWVhbl9wYWlyXyIsIGNvc3R2YXIsICJfYzI1IildCgojIGxhdGl0dWRlIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KbXlkYXRhJGxhdCA8LSBteWRhdGFbLHBhc3RlMCgibGF0X21lYW5fcGFpcl8iLCBjb3N0dmFyLCAiX2MyNSIpXQoKIyB0ZW1wZXJhdHVyZSBicmVhZHRoIC0tLS0tLS0tLS0tLS0tLS0tCm15ZGF0YSR0YXNfYnJlYWR0aCA8LSBteWRhdGEkdGFzX3JhbmdlICMgbWVhbihtZWFuKHNwMSBhbm51YWwgdGFzIHJhbmdlIC0tIG9uZSB2YWx1ZSBwZXIgY2VsbCksIG1lYW4oc3AyIGFubnVhbCB0YXMgcmFuZ2UgLS0gb25lIHZhbHVlIHBlciBjZWxsKSkKCiMgbWVhbiBhbm51YWwgdGVtcGVyYXR1cmUgLS0tLS0tLS0tLS0tLS0KbXlkYXRhJHRhc19wb3NpdGlvbiA8LSBteWRhdGEkdGFzX21lYW4gIyBtZWFuKG1lYW4oc3AxIGFubnVhbCB0YXMgbWVhbiAgLS0gb25lIHZhbHVlIHBlciBjZWxsKSwgbWVhbihzcDIgYW5udWFsIHRhcyBtZWFuICAtLSBvbmUgdmFsdWUgcGVyIGNlbGwpKQoKIyBwcmVjaXBpdGF0aW9uIGJyZWFkdGggLS0tLS0tLS0tLS0tLS0tIApteWRhdGEkcGNwX2JyZWFkdGggPC0gbXlkYXRhJHBjcF9yYW5nZSAjIG1lYW4obWVhbihzcDEgYW5udWFsIHBjcCByYW5nZSAgLS0gb25lIHZhbHVlIHBlciBjZWxsKSwgbWVhbihzcDIgYW5udWFsIHBjcCByYW5nZSAgLS0gb25lIHZhbHVlIHBlciBjZWxsKSkKCiMgcHJlY2lwaXRhdGlvbiBicmVhZHRoIC0tLS0tLS0tLS0tLS0tLSAKbXlkYXRhJHBjcF9wb3NpdGlvbiA8LSBteWRhdGEkcGNwX21lYW4gIyBtZWFuKG1lYW4oc3AxIGFubnVhbCBwY3AgbWVhbiAgLS0gb25lIHZhbHVlIHBlciBjZWxsKSwgbWVhbihzcDIgYW5udWFsIHBjcCBtZWFuICAtLSBvbmUgdmFsdWUgcGVyIGNlbGwpKQoKIyBkaXN0YW5jZSAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQpteWRhdGEkZGlzdGFuY2UgPC0gbXlkYXRhWyxwYXN0ZTAoImNlbnRyb2lkX2Rpc3RhbmNlXyIsY29zdHZhciwiX2MyNSIpXQoKIyBtb3VudGFpbiBtYXNzIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQptdG5zIDwtIHJlYWRPR1IoZHNuPSJ+L0JveCBTeW5jL0NCX1ZGX1NoYXJlZC9EcnlfTGFiL1Byb2plY3RzL0pNUEgvT3RoZXJfSW5wdXRfRGF0YS9HTUJBIiwgbGF5ZXI9IkdNQkEgTW91bnRhaW4gSW52ZW50b3J5X3YxLjItV29ybGQiLCB2ZXJib3NlID0gRkFMU0UpCndkUEFNIDwtICIvVXNlcnMvYm90ZXJvbGFiMS9Cb3ggU3luYy9DQl9WRl9TaGFyZWQvRHJ5X0xhYi9Qcm9qZWN0cy9KTVBIL1BSRVAvUEFNL0RhdGEiCnNldHdkKHdkUEFNKTsgbG9hZCgiTG9uTGF0X0JpcmRQQU1fcmFzdGVyLnJkYXRhIikKbXRucyA8LSByYXN0ZXJpemUobXRucywgTG9uTGF0X0JpcmRQQU1fcmFzdGVyKQptdG5zQGRhdGFAdmFsdWVzWyFpcy5uYShtdG5zQGRhdGFAdmFsdWVzKV0gPC0gMSAjIHJlcGxhY2UgbW91bnRhaW4gSURzIHdpdGggc2ltcGxlIGNvZGluZy4gMSBmb3IgbW91bnRhaW4uLi4KbXRuc0BkYXRhQHZhbHVlc1tpcy5uYShtdG5zQGRhdGFAdmFsdWVzKV0gPC0gMCAjIC4uLjAgZm9yIG5vIG1vdW50YWluCgpteWRhdGEkbXRuX21hc3MgPC0gTkEKZm9yIChpIGluIDE6bnJvdyhteWRhdGEpKSB7CiAgY29vcmRzMTwtZGF0YS5mcmFtZShsb249bXlkYXRhJGxvbltpXSwgbGF0PW15ZGF0YSRsYXRbaV0pOyBjb29yZGluYXRlcyhjb29yZHMxKTwtYygibG9uIiwibGF0Iik7IGNycyhjb29yZHMxKTwtY3JzKExvbkxhdF9CaXJkUEFNX3Jhc3RlcikgIyBnZXQgY29vcmRpbmF0ZXMKICB6IDwtIGV4dHJhY3QobXRucywgY29vcmRzMSwgYnVmZmVyID0gcmFzdGVyOjpwb2ludERpc3RhbmNlKGMoMCwwKSwgYygwLDgpLCBsb25sYXQgPSBUKSkKICBteWRhdGEkbXRuX21hc3NbaV0gPC0gc3VtKHpbWzFdXSkgLyBsZW5ndGgoeltbMV1dKQp9CgpyYXN0ZXI6OnBvaW50RGlzdGFuY2UoYygwLDApLCBjKDAsOCksIGxvbmxhdCA9IFQpICMgY29ycmVzcG9uZHMgdG8gYSByYWRpdXMgb2YganVzdCBhYm91dCA4IGRlZ3JlZXMuCnJhc3Rlcjo6cG9pbnREaXN0YW5jZShjKDc1LDc1KSwgYyg3NSwoNzUrOCkpLCBsb25sYXQgPSBUKSAjIHBvbGFyIGNpcmNsZXMgYXJlIGJpZ2dlciwgYnV0IG5vdCB0aGF0IG11Y2ggYmlnZ2VyLiBzbyBzaG91bGQgYmUgT0suCnJhc3Rlcjo6cGxvdChtdG5zKQpyYXN0ZXI6OnBsb3Qod29ybGQkZ2VvbWV0cnksIGFkZCA9IFQpCnBsb3RyaXg6OmRyYXcuY2lyY2xlKDcwLCA0MCwgOCwgbnYgPSAxMDAwLCBib3JkZXIgPSAiaG90cGluayIsIGx0eSA9IDEsIGx3ZCA9IDEpCnBsb3RyaXg6OmRyYXcuY2lyY2xlKDE0MCwgLTUsIDgsIG52ID0gMTAwMCwgYm9yZGVyID0gImhvdHBpbmsiLCBsdHkgPSAxLCBsd2QgPSAxKQpwbG90cml4OjpkcmF3LmNpcmNsZSgtODAsIDEwLCA4LCBudiA9IDEwMDAsIGJvcmRlciA9ICJob3RwaW5rIiwgbHR5ID0gMSwgbHdkID0gMSkKcGxvdHJpeDo6ZHJhdy5jaXJjbGUoLTcwLCAtNTAsIDgsIG52ID0gMTAwMCwgYm9yZGVyID0gImhvdHBpbmsiLGx0eSA9IDEsIGx3ZCA9IDEpCgp4IDwtIGdncGxvdCh3b3JsZCkrCiAgZ2VvbV9zZigpICsKICBnZW9tX3BvaW50KGRhdGEgPSBteWRhdGFbb3JkZXIobXlkYXRhWywgIm10bl9tYXNzIl0sIGRlY3JlYXNpbmcgPSBGKSxdLCBhZXMoeT1sYXQsIHg9bG9uLCBjb2xvciA9IG10bl9tYXNzKSwgYWxwaGEgPSAwLjkpICsKICBnZ3RpdGxlKGkpK3NjYWxlX2NvbG9yX3ZpcmlkaXMoKQpwcmludCh4KQoKIyB3YXRlciBidWZmZXJpbmcgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQp3dHIgPC0gcmVhZF9zZigifi9Cb3ggU3luYy9DQl9WRl9TaGFyZWQvRHJ5X0xhYi9Qcm9qZWN0cy9KTVBIL090aGVyX0lucHV0X0RhdGEvbmVfNTBtX29jZWFuL25lXzUwbV9vY2Vhbi5zaHAiKQp0ZW1wIDwtIG10bnMKdmFsdWVzKHRlbXApIDwtIDEwMAp0ZW1wIDwtIG1hc2sodGVtcCwgd3RyKQp3dHIgPC0gbXRucwp2YWx1ZXMod3RyKSA8LSAwCnd0clt0ZW1wID09IDEwMF0gPC0gMTsgcm0odGVtcCkKcGxvdCh3dHIpCgpteWRhdGEkd3RyY2VsbHMgPC0gTkEKbXlkYXRhJHd0cm1hc3MgPC0gTkEKZm9yIChpIGluIDE6bnJvdyhteWRhdGEpKSB7CiAgY29vcmRzMTwtZGF0YS5mcmFtZShsb249bXlkYXRhJGxvbltpXSwgbGF0PW15ZGF0YSRsYXRbaV0pOyBjb29yZGluYXRlcyhjb29yZHMxKTwtYygibG9uIiwibGF0Iik7IGNycyhjb29yZHMxKTwtY3JzKExvbkxhdF9CaXJkUEFNX3Jhc3RlcikgIyBnZXQgY29vcmRpbmF0ZXMKICB6IDwtIGV4dHJhY3Qod3RyLCBjb29yZHMxLCBidWZmZXIgPSByYXN0ZXI6OnBvaW50RGlzdGFuY2UoYygwLDApLCBjKDAsOCksIGxvbmxhdCA9IFQpKQogIG15ZGF0YSRtaW5pcGxvdFtpXSA8LSBsZW5ndGgoeltbMV1dKQogIG15ZGF0YSR3dHJjZWxsc1tpXSA8LSBzdW0oeltbMV1dKQogIG15ZGF0YSR3dHJtYXNzW2ldIDwtIHN1bSh6W1sxXV0pIC8gbGVuZ3RoKHpbWzFdXSkKfQpteWRhdGEkd2F0ZXJfYnVmZmVyaW5nIDwtIG15ZGF0YSR3dHJtYXNzCgp4IDwtIGdncGxvdCh3b3JsZCkrCiAgZ2VvbV9zZigpICsKICBnZW9tX3BvaW50KGRhdGEgPSBteWRhdGFbb3JkZXIobXlkYXRhWywgIndhdGVyX2J1ZmZlcmluZyJdLCBkZWNyZWFzaW5nID0gRiksXSwgYWVzKHk9bGF0LCB4PWxvbiwgY29sb3IgPSB3YXRlcl9idWZmZXJpbmcpLCBhbHBoYSA9IDAuOSkgKwogIGdndGl0bGUoaSkrc2NhbGVfY29sb3JfdmlyaWRpcygpCnByaW50KHgpCgoKIyBkaXNwZXJzYWwgYWJpbGl0eSAtLS0tLS0tLS0tLS0tLS0tLS0tLQpkaXNwYWIgPC0gcmVhZC5jc3YoIn4vQm94IFN5bmMvQ0JfVkZfU2hhcmVkL0RyeV9MYWIvUHJvamVjdHMvSk1QSC9PdGhlcl9JbnB1dF9EYXRhL0JpcmQgSGFuZC1XaW5nIEluZGV4L0RhdGFzZXQgSFdJIDIwMjAtMDQtMTAuY3N2IikKbXlkYXRhJGRpc3BlcnNhbF9hYmlsaXR5IDwtIE5BCmZvciAoaSBpbiAxOm5yb3cobXlkYXRhKSl7CiAgZGlzcGFiX3NwMSA8LSBkaXNwYWIkSFdJW2Rpc3BhYiRJVUNOLm5hbWUgPT0gbXlkYXRhJFNwZWNpZXMuMWJsW2ldXQogIGRpc3BhYl9zcDIgPC0gZGlzcGFiJEhXSVtkaXNwYWIkSVVDTi5uYW1lID09IG15ZGF0YSRTcGVjaWVzLjJibFtpXV0KICBkaXNwYWJfcGFpciA8LSBtZWFuKGMoZGlzcGFiX3NwMSwgZGlzcGFiX3NwMiksIG5hLnJtID0gVCkKICBteWRhdGEkZGlzcGVyc2FsX2FiaWxpdHlbaV0gPC0gZGlzcGFiX3BhaXIKICBybShkaXNwYWJfc3AxLCBkaXNwYWJfc3AyLCBkaXNwYWJfcGFpcikKfQpteWRhdGEgPC0gbXlkYXRhWyFpcy5uYW4obXlkYXRhJGRpc3BlcnNhbF9hYmlsaXR5KSxdCgojIHBhaXIgYWdlIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCm15ZGF0YSRwYWlyX2FnZSA8LSBteWRhdGEkUGFpci5hZ2UuLk1ZLgoKIyBsZW5ndGggb2YgYm91bmRhcnkgLS0tLS0tLS0tLS0tLS0tLS0tLQpteWRhdGEkYm91bmRhcnlfbGVuZ3RoIDwtIG15ZGF0YSRib3VuZGFyeV9sZW5ndGhfZWxlX2MyNQoKIyByZXRhaW4gY29scyBvZiBpbnRlcmVzdCBvbmx5LgpteWRhdGEgPC0gbXlkYXRhWyxjKCJ1bmlxdWVQYWlySWQiLCAiU3BlY2llcy4xIiwgIlNwZWNpZXMuMiIsICJTcGVjaWVzLjFibCIsICJTcGVjaWVzLjJibCIsICJjb3N0IiwgImxhdCIsICJsb24iLAogICAgICAgICAgICAgICAgICAgICJ0YXNfYnJlYWR0aCIsInRhc19wb3NpdGlvbiIsICJwY3BfYnJlYWR0aCIsInBjcF9wb3NpdGlvbiIsICJtdG5fbWFzcyIsICJ3YXRlcl9idWZmZXJpbmciLCAKICAgICAgICAgICAgICAgICAgICAiZGlzcGVyc2FsX2FiaWxpdHkiLCAicGFpcl9hZ2UiLCAiZGlzdGFuY2UiLCAiYm91bmRhcnlfbGVuZ3RoIiwgIk1BVF9vdmVybGFwIiwgInJlYWxtIiwKICAgICAgICAgICAgICAgICAgICAibGFuZGdhcCIpXQpgYGAKCmxvYWQgcGh5bG8gYW5kIHBydW5lCmBgYHtyfQojIHBoeWxvIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQpzZXR3ZCgifi9Cb3ggU3luYy9DQl9WRl9TaGFyZWQvRHJ5X0xhYi9Qcm9qZWN0cy9KTVBIL090aGVyX0lucHV0X0RhdGEvQmlyZFRyZWVzIikKbG9hZChmaWxlID0gIkJpcmRUcmVlcy5SZGF0YSIpCnRyZWUgPC0gdHJlZXNbWzFdXTsgcm0odHJlZXMpICMgcGljayB0cmVlIChWRiBHRVQgVFJFRVMgRlJPTSBDT09ORVkhISEgYW5kIHVzZSBNQ0MgdHJlZS4pIC0tIGN1cnJlbnRseSBqdXN0IHVzaW5nIHRyZWUgMSBoZXJlLiAKdHJlZSA8LSBkcm9wLnRpcCh0cmVlLCB0cmVlJHRpcC5sYWJlbFt3aGljaCh0cmVlJHRpcC5sYWJlbCAlbm90aW4lIG15ZGF0YSRTcGVjaWVzLjEpXSkgIyBpbml0aWFsIG5hbWUgbWF0Y2hpbmcuCm15ZGF0YSA8LSBteWRhdGFbd2hpY2gobXlkYXRhJFNwZWNpZXMuMSAlaW4lIHRyZWUkdGlwLmxhYmVsKSxdCm15ZGF0YSA8LSBteWRhdGFbbWF0Y2godHJlZSR0aXAubGFiZWwsIG15ZGF0YSRTcGVjaWVzLjEpLF0Kc3VtKG15ZGF0YSRTcGVjaWVzLjEgIT0gdHJlZSR0aXAubGFiZWwpICMgc29ydGVkIChidXQgc3RpbGwgc3BlY2lmeSBmb3JtIGJlbG93IGZvciBzYWZldHkpCmBgYAoKU2F2ZSBmcmVhbWUgZm9yIENCCmBgYHtyfQpzZXR3ZCgifi9Cb3ggU3luYy9DQl9WRl9TaGFyZWQvRHJ5X0xhYi9Qcm9qZWN0cy9KTVBIL0FuYWx5emVfUHJvY2Vzc2VkX0NsdXN0ZXJfT3V0cHV0cy9EYXRhIikKc2F2ZShteWRhdGEsIGZpbGUgPSAiZWxlX2RhdGFfZm9yX0NCLnJkYXRhIikKd3JpdGUuY3N2KG15ZGF0YSwgZmlsZSA9ICJlbGVfZGF0YV9mb3JfQ0IuY3N2IikKYGBgCgoKCk5laWdoYm9ycyBmb3Igc3B0aWFsIGFuYWx5c2lzLiAKYGBge3J9CmRpc3RtIDwtIG15ZGF0YVssYygibG9uIiwgImxhdCIpXQpkaXN0bSA8LSBnZW9kaXN0KGRpc3RtLCBtZWFzdXJlID0gImdlb2Rlc2ljIikKcm93bmFtZXMoZGlzdG0pIDwtIHJvd25hbWVzKG15ZGF0YSkKY29sbmFtZXMoZGlzdG0pIDwtIHJvd25hbWVzKG15ZGF0YSkKYmFzZWNvbHMgPC0gbmNvbChteWRhdGEpCgojIG5laWdodGJvcnMKY29vcmRzPC1jYmluZChteWRhdGEkbG9uLCBteWRhdGEkbGF0KTsgY29vcmRzPC1hcy5tYXRyaXgoY29vcmRzKSA7IHJvdy5uYW1lcyhjb29yZHMpPC1yb3duYW1lcyhteWRhdGEpCmsxIDwtIGtubjJuYihrbmVhcm5laWdoKGNvb3JkcywgbG9uZ2xhdCA9IFQpKQpuYjwtIGRuZWFybmVpZ2goY29vcmRzLHJvdy5uYW1lcyA9IHJvdy5uYW1lcyhjb29yZHMpLCBkMT0wLGQyPW1heCh1bmxpc3QobmJkaXN0cyhrMSwgY29vcmRzLCBsb25nbGF0ID0gVCkpKSxsb25nbGF0PVQpCmBgYAoKCnBsb3RzIGZvciB0cmFuc2Zvcm1hdGlvbnMKYGBge3J9CmZvciAoaSBpbiBjKCJjb3N0IiwgImxhdCIsICJsb24iLCJ0YXNfYnJlYWR0aCIsInRhc19wb3NpdGlvbiIsInBjcF9icmVhZHRoIiwicGNwX3Bvc2l0aW9uIiwgIm10bl9tYXNzIiwgImRpc3BlcnNhbF9hYmlsaXR5IiwgInBhaXJfYWdlIiwgImRpc3RhbmNlIiwgImJvdW5kYXJ5X2xlbmd0aCIsICJNQVRfb3ZlcmxhcCIpKSB7Cmhpc3QobXlkYXRhWywgaV0sIGJyZWFrcyA9IDUwLCBtYWluID0gaSkKfQpgYGAKCndvcmxkIHBsb3RzCmBgYHtyfQpmb3IgKGkgaW4gYygiY29zdCIsICJsYXQiLCAibG9uIiwidGFzX2JyZWFkdGgiLCJ0YXNfcG9zaXRpb24iLCJwY3BfYnJlYWR0aCIsInBjcF9wb3NpdGlvbiIsICJtdG5fbWFzcyIsICJkaXNwZXJzYWxfYWJpbGl0eSIsICJwYWlyX2FnZSIsICJkaXN0YW5jZSIsICJib3VuZGFyeV9sZW5ndGgiLCAiTUFUX292ZXJsYXAiLCAicmVhbG0iLCAibGFuZGdhcCIpKSB7CiAgaWYoaSAlaW4lIGMoImNvc3QiKSl7CiAgICBteWMgPC0gbXlkYXRhWywgaV07IG15YyA8LSB2bG9nKG15YykKICAgIHggPC0gZ2dwbG90KHdvcmxkKSsKICAgICAgZ2VvbV9zZigpICsKICAgICAgZ2VvbV9wb2ludChkYXRhID0gbXlkYXRhW29yZGVyKG15ZGF0YVssIGldLCBkZWNyZWFzaW5nID0gRiksXSwgYWVzKHk9bGF0LCB4PWxvbiwgY29sb3IgPSBteWMpLCBhbHBoYSA9IDAuNzUpICsKICAgICAgZ2d0aXRsZShpKSsKICAgICAgc2NhbGVfY29sb3JfdmlyaWRpcygpCiAgICBwcmludCh4KQogIH0gZWxzZSBpZiAoaSAlaW4lIGMoInJlYWxtMSIsICJyZWFsbTIiKSl7CiAgICB4IDwtIGdncGxvdCh3b3JsZCkrCiAgICAgIGdlb21fc2YoKSArIAogICAgICBnZW9tX3BvaW50KGRhdGEgPSBteWRhdGFbb3JkZXIobXlkYXRhWywgImNvc3QiXSwgZGVjcmVhc2luZyA9IEYpLF0sIGFlcyh5PWxhdCwgeD1sb24sIGNvbG9yID0gZ2V0KGkpKSwgYWxwaGEgPSAwLjc1KSArCiAgICAgIGdndGl0bGUocGFzdGUwKCJsbiAiLCBpKSkrCiAgICAgIHNjYWxlX2NvbG9yX3ZpcmlkaXMoZGlzY3JldGUgPSBUKQogICAgcHJpbnQoeCkgCiAgfSBlbHNlIHsKICAgIHggPC0gZ2dwbG90KHdvcmxkKSsKICAgICAgZ2VvbV9zZigpICsgCiAgICAgIGdlb21fcG9pbnQoZGF0YSA9IG15ZGF0YVtvcmRlcihteWRhdGFbLCAiY29zdCJdLCBkZWNyZWFzaW5nID0gRiksXSwgYWVzKHk9bGF0LCB4PWxvbiwgY29sb3IgPSBnZXQoaSkpLCBhbHBoYSA9IDAuNzUpICsKICAgICAgZ2d0aXRsZShpKSsKICAgICAgc2NhbGVfY29sb3JfdmlyaWRpcygpCiAgICBwcmludCh4KSAKICB9Cn0KYGBgCgoKYGBge3J9Cm15ZGF0YSA8LSBteWRhdGFbLCAxOmJhc2Vjb2xzXQojIG5vIHNwYXRpYWwgZmlsdGVyaW5nIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQptIDwtIGdscyhzY2FsZShJKHZsb2coY29zdCkpKSB+IHNjYWxlKEkodmxvZyh0YXNfYnJlYWR0aCkpKSwgY29ycmVsYXRpb24gPSBjb3JQYWdlbCgwLjk5LCBwaHkgPSB0cmVlLCBmaXhlZCA9IEYsIGZvcm0gPSB+U3BlY2llcy4xKSwgZGF0YSA9IG15ZGF0YSwgbWV0aG9kID0gIlJFTUwiKTtzdW1tYXJ5KG0pCnByaW50KHBhc3RlKCJDb3JyZWxhdGlvbiBiZXR3ZWVuIGRhdGEgYW5kIHByZWRpY3Rpb246ICAiLCBjb3IocHJlZGljdChtKSxzY2FsZShJKHZsb2cobXlkYXRhJGNvc3QpKSkpKSkKaGlzdChyZXNpZChtKSkKcGxvdChtLCByZXNpZCguLCB0eXBlID0gInAiKSB+IGZpdHRlZCguKSwgYWJsaW5lID0gMCkKcGxvdChtLCBzY2FsZShJKHZsb2coY29zdCkpKSB+IGZpdHRlZCguKSwgYWJsaW5lID0gYygwLDEpKQpxcW5vcm0obSkKCiMgcGxvdCAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KcGxvdChzY2FsZShJKHZsb2cobXlkYXRhJHRhc19icmVhZHRoKSkpLCBzY2FsZShJKHZsb2cobXlkYXRhJGNvc3QpKSksIHBjaCA9IDE2LAogICAgIHhsYWIgPSAidGhlcm1hbCBuaWNoZSBicmVhZHRoIiwgeWxhYiA9IHBhc3RlMCgibG9nICIsIGNvc3R2YXIsICIgY29zdCIgKSwgbWFpbiA9ICJHbG9iYWwgbW9kZWwiKQpteXggPC0gc2VxKG1pbihzY2FsZSh2bG9nKG15ZGF0YSR0YXNfYnJlYWR0aCkpKSwgbWF4KHNjYWxlKHZsb2cobXlkYXRhJHRhc19icmVhZHRoKSkpLCBsZW5ndGgub3V0PTEwMCkgIyBwbG90IGZpdApteWNvZWZzPC1jb2VmKG0pCm15eSA8LSBteWNvZWZzWzFdICsgbXljb2Vmc1syXSpteXgKbGluZXMoYygwLDApLCBjKC0xMCwxMDApLCBsdHk9MikKbGluZXMobXl4LG15eSxjb2wgPSAncmVkJykKbSA8LSBnbHMoc2NhbGUoSSh2bG9nKGNvc3QpKSkgfiBzY2FsZShJKGxvZyh0YXNfYnJlYWR0aCkpKSwgZGF0YSA9IG15ZGF0YSwgbWV0aG9kID0gIlJFTUwiKQpteXggPC0gc2VxKG1pbihzY2FsZSh2bG9nKG15ZGF0YSR0YXNfYnJlYWR0aCkpKSwgbWF4KHNjYWxlKHZsb2cobXlkYXRhJHRhc19icmVhZHRoKSkpLCBsZW5ndGgub3V0PTEwMCkgIyBwbG90IGZpdApteWNvZWZzPC1jb2VmKG0pCm15eSA8LSBteWNvZWZzWzFdICsgbXljb2Vmc1syXSpteXgKbGluZXMoYygwLDApLCBjKC0xMCwxMDApLCBsdHk9MikKbGluZXMobXl4LG15eSxjb2wgPSAncmVkJywgbHR5PTIpCgoKCgojIHdpdGggc3BhdGlhbCBmaWx0ZXJpbmcgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQptIDwtIGdscyhzY2FsZShJKHZsb2coY29zdCkpKSB+IHNjYWxlKEkodmxvZyh0YXNfYnJlYWR0aCkpKSwgY29ycmVsYXRpb24gPSBjb3JQYWdlbCgwLjk5LCBwaHkgPSB0cmVlLCBmaXhlZCA9IEYsIGZvcm0gPSB+U3BlY2llcy4xKSwgZGF0YSA9IG15ZGF0YSwgbWV0aG9kID0gIlJFTUwiKQojIHNwYXRpYWwgYXV0b2NvcnIgLS0tLS0tLS0tLS0tLS0tLS0tLS0tCm1hdHggPC0gYXMubWF0cml4KG0kcmVzaWR1YWxzKTsgcm93bmFtZXMobWF0eCkgPC0gcm93bmFtZXMobXlkYXRhKQpzcGFjIDwtIGxldHMuY29ycmVsKHg9bWF0eCwgeT1kaXN0bSwgej0xMiwgZXF1aWRpc3RhbnQgPSBULCBwbG90ID0gVCkKbW9yYW4udGVzdChyZXNpZHVhbHMobSksIG5iMmxpc3R3KG5iKSkkcC52YWx1ZSAjIG5vIGV2aWRlbmNlIG9mIHNwYXRpYWwgY29tcG9uZW50Li4gCgojIHNwYXRpYWwgZmlsdGVyaW5nIC0tLS0tLS0tLS0tLS0tLS0tLS0tCnJtKHNhcmNvbCkKc2FyY29sIDwtIFNwYXRpYWxGaWx0ZXJpbmcoZm9ybXVsYSA9IHNjYWxlKEkodmxvZyhjb3N0KSkpIH4gc2NhbGUoSSh2bG9nKHRhc19icmVhZHRoKSkpLAogICAgICAgICAgICAgICAgICAgICAgICAgICBkYXRhID0gbXlkYXRhLG5iPW5iLCBzdHlsZT0iVyIsIEV4YWN0RVYgPSBUUlVFKQpteWRhdGFbLGMoKGJhc2Vjb2xzKzEpOihiYXNlY29scysxKyBkaW0oZml0dGVkKHNhcmNvbCkpWzJdLTEpKV08LWZpdHRlZChzYXJjb2wpCmNvbG5hbWVzKG15ZGF0YSkgIyA0IHZlY3RvcnMgY3JlYXRlZC4KCiMgdmVjdG9yIDEgYWRkZWQgLS0tCm0gPC0gZ2xzKHNjYWxlKEkodmxvZyhjb3N0KSkpIH4gc2NhbGUoSSh2bG9nKHRhc19icmVhZHRoKSkpICsgVjIzICsgVjI0ICsgVjI1ICsgVjI2ICsgVjI3ICsgVjI4ICsgVjI5ICsgVjMwICsgVjMxLCBjb3JyZWxhdGlvbiA9IGNvclBhZ2VsKDAuOTksIHBoeSA9IHRyZWUsZml4ZWQgPSBGLCBmb3JtID0gflNwZWNpZXMuMSksIGRhdGEgPSBteWRhdGEsIG1ldGhvZCA9ICJSRU1MIik7IHN1bW1hcnkobSk7IGNvcihwcmVkaWN0KG0pLHNjYWxlKEkodmxvZyhteWRhdGEkY29zdCkpKSkKbWF0eCA8LSBhcy5tYXRyaXgobSRyZXNpZHVhbHMpOyByb3duYW1lcyhtYXR4KSA8LSByb3duYW1lcyhteWRhdGEpCnNwYWMgPC0gbGV0cy5jb3JyZWwoeD1tYXR4LCB5PWRpc3RtLCB6PTEyLCBlcXVpZGlzdGFudCA9IFQsIHBsb3QgPSBUKQptb3Jhbi50ZXN0KHJlc2lkdWFscyhtKSwgbmIybGlzdHcobmIpKSRwLnZhbHVlICMgdG9vIG11Y2guCmhpc3QocmVzaWQobSkpCnBsb3QobSwgcmVzaWQoLiwgdHlwZSA9ICJwIikgfiBmaXR0ZWQoLiksIGFibGluZSA9IDApCnBsb3QobSwgc2NhbGUoSSh2bG9nKGNvc3QpKSkgfiBmaXR0ZWQoLiksIGFibGluZSA9IGMoMCwxKSkKcXFub3JtKG0pCgpmb3IoaSBpbiBjKCJWMjMiLCAiVjI0IiwgIlYyNSIsICJWMjYiLCAiVjI3IiwgIlYyOCIsICJWMjkiLCAiVjMwIiwgIlYzMSIpKXsKICB4IDwtIGdncGxvdCh3b3JsZCkrCiAgZ2VvbV9zZigpICsKICBnZW9tX3BvaW50KGRhdGEgPSBteWRhdGFbb3JkZXIobXlkYXRhWyxpXSksXSwgYWVzKHk9bGF0LCB4PWxvbiwgY29sb3IgPSBteWRhdGFbLGldKSwgYWxwaGEgPSAwLjkpICsKICBzY2FsZV9jb2xvcl92aXJpZGlzKCkKICBwcmludCh4KQp9CmBgYAoKCklzIHRoZXJtYWwgbmljaGUgYnJlYWR0aCBwcmVkaWN0ZWQgYnkgbGF0aXR1ZGUgcGx1cyBsYXRpdHVkZTI/IApgYGB7cn0KbXlkYXRhIDwtIG15ZGF0YVssIDE6YmFzZWNvbHNdCm0gPC0gZ2xzKHNjYWxlKEkodmxvZyh0YXNfYnJlYWR0aCkpKSB+IHNjYWxlKGxhdCkgKyBzY2FsZShJKGxhdF4yKSksIGNvcnJlbGF0aW9uID0gY29yUGFnZWwoMC45OSwgcGh5ID0gdHJlZSwgZml4ZWQgPSBGLCBmb3JtID0gflNwZWNpZXMuMSksIGRhdGEgPSBteWRhdGEsIG1ldGhvZCA9ICJSRU1MIik7IHN1bW1hcnkobSkKcHJpbnQocGFzdGUoIkNvcnJlbGF0aW9uIGJldHdlZW4gZGF0YSBhbmQgcHJlZGljdGlvbjogICIsIGNvcihwcmVkaWN0KG0pLHNjYWxlKEkodmxvZyhteWRhdGEkdGFzX2JyZWFkdGgpKSkpKSkKaGlzdChyZXNpZChtKSkKcGxvdChtLCByZXNpZCguLCB0eXBlID0gInAiKSB+IGZpdHRlZCguKSwgYWJsaW5lID0gMCkKcGxvdChtLCBzY2FsZShJKHZsb2coY29zdCkpKSB+IGZpdHRlZCguKSwgYWJsaW5lID0gYygwLDEpKQpxcW5vcm0obSkKCiMgcGxvdCAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KcGxvdChzY2FsZShteWRhdGEkbGF0KSwgc2NhbGUoSSh2bG9nKG15ZGF0YSR0YXNfYnJlYWR0aCkpKSwgcGNoID0gMTYsCiAgICAgeGxhYiA9ICJsYXRpdHVkZSIsIHlsYWIgPSAidGhlcm1hbCBuaWNoZSBicmVhZHRoIiwgbWFpbiA9ICJHbG9iYWwgbW9kZWwiKQpteXggPC0gc2VxKG1pbihzY2FsZShteWRhdGEkbGF0KSksIG1heChzY2FsZShteWRhdGEkbGF0KSksIGxlbmd0aC5vdXQ9MTAwKSAjIHBsb3QgZml0Cm15Y29lZnM8LWNvZWYobSkKbXl5IDwtIG15Y29lZnNbMV0gKyBteWNvZWZzWzJdKm15eCArIG15Y29lZnNbM10qbXl4XjIKbGluZXMoYygwLDApLCBjKC0xMCwxMDApLCBsdHk9MikKbGluZXMobXl4LG15eSxjb2wgPSAncmVkJykKbSA8LSBnbHMoc2NhbGUoSSh2bG9nKHRhc19icmVhZHRoKSkpIH4gc2NhbGUobGF0KSArIHNjYWxlKEkobGF0XjIpKSwgZGF0YSA9IG15ZGF0YSwgbWV0aG9kID0gIlJFTUwiKQpteXggPC0gc2VxKG1pbihzY2FsZShteWRhdGEkbGF0KSksIG1heChzY2FsZShteWRhdGEkbGF0KSksIGxlbmd0aC5vdXQ9MTAwKSAjIHBsb3QgZml0Cm15Y29lZnM8LWNvZWYobSkKbXl5IDwtIG15Y29lZnNbMV0gKyBteWNvZWZzWzJdKm15eCArIG15Y29lZnNbM10qbXl4XjIKbGluZXMoYygwLDApLCBjKC0xMCwxMDApLCBsdHk9MikKbGluZXMobXl4LG15eSxjb2wgPSAncmVkJywgbHR5PTIpCgoKCgoKCiMgd2l0aCBzcGF0aWFsIGZpbHRlcmluZyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCm0gPC0gZ2xzKHNjYWxlKEkodmxvZyh0YXNfYnJlYWR0aCkpKSB+IHNjYWxlKGxhdCkgKyBzY2FsZShJKGxhdF4yKSksIGNvcnJlbGF0aW9uID0gY29yUGFnZWwoMC45OSwgcGh5ID0gdHJlZSwgZml4ZWQgPSBGLCBmb3JtID0gflNwZWNpZXMuMSksIGRhdGEgPSBteWRhdGEsIG1ldGhvZCA9ICJSRU1MIikKIyBzcGF0aWFsIGF1dG9jb3JyIC0tLS0tLS0tLS0tLS0tLS0tLS0tLQptYXR4IDwtIGFzLm1hdHJpeChtJHJlc2lkdWFscyk7IHJvd25hbWVzKG1hdHgpIDwtIHJvd25hbWVzKG15ZGF0YSkKc3BhYyA8LSBsZXRzLmNvcnJlbCh4PW1hdHgsIHk9ZGlzdG0sIHo9MTIsIGVxdWlkaXN0YW50ID0gVCwgcGxvdCA9IFQpCm1vcmFuLnRlc3QocmVzaWR1YWxzKG0pLCBuYjJsaXN0dyhuYikpJHAudmFsdWUgIyBubyBldmlkZW5jZSBvZiBzcGF0aWFsIGNvbXBvbmVudC4uIAoKIyBzcGF0aWFsIGZpbHRlcmluZyAtLS0tLS0tLS0tLS0tLS0tLS0tLQpybShzYXJjb2wpCnNhcmNvbCA8LSBTcGF0aWFsRmlsdGVyaW5nKGZvcm11bGEgPSBzY2FsZShJKHZsb2codGFzX2JyZWFkdGgpKSkgfiBzY2FsZShsYXQpICsgc2NhbGUoSShsYXReMikpLAogICAgICAgICAgICAgICAgICAgICAgICAgICBkYXRhID0gbXlkYXRhLG5iPW5iLCBzdHlsZT0iVyIsIEV4YWN0RVYgPSBUUlVFKQpteWRhdGFbLGMoKGJhc2Vjb2xzKzEpOihiYXNlY29scysxKyBkaW0oZml0dGVkKHNhcmNvbCkpWzJdLTEpKV08LWZpdHRlZChzYXJjb2wpCmNvbG5hbWVzKG15ZGF0YSkgIyA0IHZlY3RvcnMgY3JlYXRlZC4KCiMgdmVjdG9yIDEgYWRkZWQgLS0tCm0gPC0gZ2xzKHNjYWxlKEkodmxvZyh0YXNfYnJlYWR0aCkpKSB+IHNjYWxlKGxhdCkgKyBzY2FsZShJKGxhdF4yKSkgKyBWMjMgKyBWMjQgKyBWMjUgKyBWMjYgKyBWMjcgKyBWMjggKyBWMjkgKyBWMzAgKyBWMzEgKyBWMzIgKyBWMzMgKyBWMzQgKyBWMzUgKyBWMzYgKyBWMzcgKyBWMzgsIGNvcnJlbGF0aW9uID0gY29yUGFnZWwoMC45OSwgcGh5ID0gdHJlZSxmaXhlZCA9IEYsIGZvcm0gPSB+U3BlY2llcy4xKSwgZGF0YSA9IG15ZGF0YSwgbWV0aG9kID0gIlJFTUwiKTsgc3VtbWFyeShtKTsgY29yKHByZWRpY3QobSksc2NhbGUoSSh2bG9nKG15ZGF0YSRjb3N0KSkpKQptYXR4IDwtIGFzLm1hdHJpeChtJHJlc2lkdWFscyk7IHJvd25hbWVzKG1hdHgpIDwtIHJvd25hbWVzKG15ZGF0YSkKc3BhYyA8LSBsZXRzLmNvcnJlbCh4PW1hdHgsIHk9ZGlzdG0sIHo9MTIsIGVxdWlkaXN0YW50ID0gVCwgcGxvdCA9IFQpCm1vcmFuLnRlc3QocmVzaWR1YWxzKG0pLCBuYjJsaXN0dyhuYikpJHAudmFsdWUgIyB0b28gbXVjaC4KaGlzdChyZXNpZChtKSkKcGxvdChtLCByZXNpZCguLCB0eXBlID0gInAiKSB+IGZpdHRlZCguKSwgYWJsaW5lID0gMCkKcGxvdChtLCBzY2FsZShJKHZsb2coY29zdCkpKSB+IGZpdHRlZCguKSwgYWJsaW5lID0gYygwLDEpKQpxcW5vcm0obSkKCmZvcihpIGluIGMoIlYyMyIsICJWMjQiLCAiVjI1IiwgIlYyNiIsICJWMjciLCAiVjI4IiwgIlYyOSIsICJWMzAiLCAiVjMxIiwgIlYzMiIsICJWMzMiLCAiVjM0IiwgIlYzNSIsICJWMzYiLCAiVjM3IiwgIlYzOCIpKXsKICB4IDwtIGdncGxvdCh3b3JsZCkrCiAgZ2VvbV9zZigpICsKICBnZW9tX3BvaW50KGRhdGEgPSBteWRhdGFbb3JkZXIobXlkYXRhWyxpXSksXSwgYWVzKHk9bGF0LCB4PWxvbiwgY29sb3IgPSBteWRhdGFbLGldKSwgYWxwaGEgPSAwLjkpICsKICBzY2FsZV9jb2xvcl92aXJpZGlzKCkKICBwcmludCh4KQp9CmBgYAoKSXMgdGhlcm1hbCBuaWNoZSBicmVhZHRoIHByZWRpY3RlZCBieSBsYXRpdHVkZSBwbHVzIGxhdGl0dWRlMj8gCmBgYHtyfQpteWRhdGEgPC0gbXlkYXRhWywgMTpiYXNlY29sc10KbSA8LSBnbHMoc2NhbGUoSSh2bG9nKGNvc3QpKSkgfiBzY2FsZShsYXQpICsgc2NhbGUoSShsYXReMikpLCBjb3JyZWxhdGlvbiA9IGNvclBhZ2VsKDAuOTksIHBoeSA9IHRyZWUsIGZpeGVkID0gRiwgZm9ybSA9IH5TcGVjaWVzLjEpLCBkYXRhID0gbXlkYXRhLCBtZXRob2QgPSAiUkVNTCIpOyBzdW1tYXJ5KG0pCnByaW50KHBhc3RlKCJDb3JyZWxhdGlvbiBiZXR3ZWVuIGRhdGEgYW5kIHByZWRpY3Rpb246ICAiLCBjb3IocHJlZGljdChtKSxzY2FsZShJKHZsb2cobXlkYXRhJGNvc3QpKSkpKSkKaGlzdChyZXNpZChtKSkKcGxvdChtLCByZXNpZCguLCB0eXBlID0gInAiKSB+IGZpdHRlZCguKSwgYWJsaW5lID0gMCkKcGxvdChtLCBzY2FsZShJKHZsb2coY29zdCkpKSB+IGZpdHRlZCguKSwgYWJsaW5lID0gYygwLDEpKQpxcW5vcm0obSkKCiMgcGxvdCAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KcGxvdChzY2FsZShteWRhdGEkbGF0KSwgc2NhbGUoSSh2bG9nKG15ZGF0YSRjb3N0KSkpLCBwY2ggPSAxNiwKICAgICB4bGFiID0gImxhdGl0dWRlIiwgeWxhYiA9ICJ0aGVybWFsIG5pY2hlIGJyZWFkdGgiLCBtYWluID0gIkdsb2JhbCBtb2RlbCIpCm15eCA8LSBzZXEobWluKHNjYWxlKG15ZGF0YSRsYXQpKSwgbWF4KHNjYWxlKG15ZGF0YSRsYXQpKSwgbGVuZ3RoLm91dD0xMDApICMgcGxvdCBmaXQKbXljb2VmczwtY29lZihtKQpteXkgPC0gbXljb2Vmc1sxXSArIG15Y29lZnNbMl0qbXl4ICsgbXljb2Vmc1szXSpteXheMgpsaW5lcyhjKDAsMCksIGMoLTEwLDEwMCksIGx0eT0yKQpsaW5lcyhteXgsbXl5LGNvbCA9ICdyZWQnKQptIDwtIGdscyhzY2FsZShJKHZsb2coY29zdCkpKSB+IHNjYWxlKGxhdCkgKyBzY2FsZShJKGxhdF4yKSksIGRhdGEgPSBteWRhdGEsIG1ldGhvZCA9ICJSRU1MIikKbXl4IDwtIHNlcShtaW4oc2NhbGUobXlkYXRhJGxhdCkpLCBtYXgoc2NhbGUobXlkYXRhJGxhdCkpLCBsZW5ndGgub3V0PTEwMCkgIyBwbG90IGZpdApteWNvZWZzPC1jb2VmKG0pCm15eSA8LSBteWNvZWZzWzFdICsgbXljb2Vmc1syXSpteXggKyBteWNvZWZzWzNdKm15eF4yCmxpbmVzKGMoMCwwKSwgYygtMTAsMTAwKSwgbHR5PTIpCmxpbmVzKG15eCxteXksY29sID0gJ3JlZCcsIGx0eT0yKQoKCgoKCgojIHdpdGggc3BhdGlhbCBmaWx0ZXJpbmcgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQptIDwtIGdscyhzY2FsZShJKHZsb2coY29zdCkpKSB+IHNjYWxlKGxhdCkgKyBzY2FsZShJKGxhdF4yKSksIGNvcnJlbGF0aW9uID0gY29yUGFnZWwoMC45OSwgcGh5ID0gdHJlZSwgZml4ZWQgPSBGLCBmb3JtID0gflNwZWNpZXMuMSksIGRhdGEgPSBteWRhdGEsIG1ldGhvZCA9ICJSRU1MIikKIyBzcGF0aWFsIGF1dG9jb3JyIC0tLS0tLS0tLS0tLS0tLS0tLS0tLQptYXR4IDwtIGFzLm1hdHJpeChtJHJlc2lkdWFscyk7IHJvd25hbWVzKG1hdHgpIDwtIHJvd25hbWVzKG15ZGF0YSkKc3BhYyA8LSBsZXRzLmNvcnJlbCh4PW1hdHgsIHk9ZGlzdG0sIHo9MTIsIGVxdWlkaXN0YW50ID0gVCwgcGxvdCA9IFQpCm1vcmFuLnRlc3QocmVzaWR1YWxzKG0pLCBuYjJsaXN0dyhuYikpJHAudmFsdWUgIyBubyBldmlkZW5jZSBvZiBzcGF0aWFsIGNvbXBvbmVudC4uIAoKIyBzcGF0aWFsIGZpbHRlcmluZyAtLS0tLS0tLS0tLS0tLS0tLS0tLQpybShzYXJjb2wpCnNhcmNvbCA8LSBTcGF0aWFsRmlsdGVyaW5nKGZvcm11bGEgPSBzY2FsZShJKHZsb2coY29zdCkpKSB+IHNjYWxlKGxhdCkgKyBzY2FsZShJKGxhdF4yKSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGRhdGEgPSBteWRhdGEsbmI9bmIsIHN0eWxlPSJXIiwgRXhhY3RFViA9IFRSVUUpCm15ZGF0YVssYygoYmFzZWNvbHMrMSk6KGJhc2Vjb2xzKzErIGRpbShmaXR0ZWQoc2FyY29sKSlbMl0tMSkpXTwtZml0dGVkKHNhcmNvbCkKY29sbmFtZXMobXlkYXRhKSAjIDQgdmVjdG9ycyBjcmVhdGVkLgoKIyB2ZWN0b3IgMSBhZGRlZCAtLS0KbSA8LSBnbHMoc2NhbGUoSSh2bG9nKGNvc3QpKSkgfiBzY2FsZShsYXQpICsgc2NhbGUoSShsYXReMikpICsgVjIzICsgVjI0ICsgVjI1ICsgVjI2LCBjb3JyZWxhdGlvbiA9IGNvclBhZ2VsKDAuOTksIHBoeSA9IHRyZWUsZml4ZWQgPSBGLCBmb3JtID0gflNwZWNpZXMuMSksIGRhdGEgPSBteWRhdGEsIG1ldGhvZCA9ICJSRU1MIik7IHN1bW1hcnkobSk7IGNvcihwcmVkaWN0KG0pLHNjYWxlKEkodmxvZyhteWRhdGEkY29zdCkpKSkKbWF0eCA8LSBhcy5tYXRyaXgobSRyZXNpZHVhbHMpOyByb3duYW1lcyhtYXR4KSA8LSByb3duYW1lcyhteWRhdGEpCnNwYWMgPC0gbGV0cy5jb3JyZWwoeD1tYXR4LCB5PWRpc3RtLCB6PTEyLCBlcXVpZGlzdGFudCA9IFQsIHBsb3QgPSBUKQptb3Jhbi50ZXN0KHJlc2lkdWFscyhtKSwgbmIybGlzdHcobmIpKSRwLnZhbHVlICMgdG9vIG11Y2guCmhpc3QocmVzaWQobSkpCnBsb3QobSwgcmVzaWQoLiwgdHlwZSA9ICJwIikgfiBmaXR0ZWQoLiksIGFibGluZSA9IDApCnBsb3QobSwgc2NhbGUoSSh2bG9nKGNvc3QpKSkgfiBmaXR0ZWQoLiksIGFibGluZSA9IGMoMCwxKSkKcXFub3JtKG0pCgpmb3IoaSBpbiBjKCJWMjMiLCAiVjI0IiwgIlYyNSIsICJWMjYiKSl7CiAgeCA8LSBnZ3Bsb3Qod29ybGQpKwogIGdlb21fc2YoKSArCiAgZ2VvbV9wb2ludChkYXRhID0gbXlkYXRhW29yZGVyKG15ZGF0YVssaV0pLF0sIGFlcyh5PWxhdCwgeD1sb24sIGNvbG9yID0gbXlkYXRhWyxpXSksIGFscGhhID0gMC45KSArCiAgc2NhbGVfY29sb3JfdmlyaWRpcygpCiAgcHJpbnQoeCkKfQpgYGAKCgpgYGB7cn0KbXlkYXRhIDwtIG15ZGF0YVssIDE6YmFzZWNvbHNdCm0gPC0gZ2xzKHNjYWxlKEkodmxvZyhjb3N0KSkpIH4gc2NhbGUoSSgodGFzX2JyZWFkdGgpKSkgKyBzY2FsZShJKCh0YXNfcG9zaXRpb24pKSkgKyBzY2FsZShJKChwY3BfYnJlYWR0aCkpKSArIHNjYWxlKEkoKHBjcF9wb3NpdGlvbikpKSArIAogICAgICAgICAgIHNjYWxlKEkoKG10bl9tYXNzKSkpICsgc2NhbGUoSSgoZGlzcGVyc2FsX2FiaWxpdHkpKSkgKyBzY2FsZShJKChwYWlyX2FnZSkpKSArIHNjYWxlKEkoKGRpc3RhbmNlKSkpICsgc2NhbGUoSSgoYm91bmRhcnlfbGVuZ3RoKSkpICsgCiAgICAgICAgICAgc2NhbGUoSSgoTUFUX292ZXJsYXApKSkgKyBsYW5kZ2FwICsgcmVhbG0sCiAgICAgICAgIGNvcnJlbGF0aW9uID0gY29yUGFnZWwoMC45OSwgcGh5ID0gdHJlZSwgZml4ZWQgPSBGLCBmb3JtID0gflNwZWNpZXMuMSksIGRhdGEgPSBteWRhdGEsIG1ldGhvZCA9ICJSRU1MIik7IHN1bW1hcnkobSk7IGNhcjo6dmlmKG0pCnByaW50KHBhc3RlKCJDb3JyZWxhdGlvbiBiZXR3ZWVuIGRhdGEgYW5kIHByZWRpY3Rpb246ICAiLCBjb3IocHJlZGljdChtKSxzY2FsZShJKHZsb2cobXlkYXRhJGNvc3QpKSkpKSkKaGlzdChyZXNpZChtKSkKcGxvdChtLCByZXNpZCguLCB0eXBlID0gInAiKSB+IGZpdHRlZCguKSwgYWJsaW5lID0gMCkKcGxvdChtLCBzY2FsZShJKHZsb2coY29zdCkpKSB+IGZpdHRlZCguKSwgYWJsaW5lID0gYygwLDEpKQpxcW5vcm0obSkKCgojIHNwYXRpYWwgYXV0b2NvcnIgLS0tLS0tLS0tLS0tLS0tLS0tLS0tCm1hdHggPC0gYXMubWF0cml4KG0kcmVzaWR1YWxzKTsgcm93bmFtZXMobWF0eCkgPC0gcm93bmFtZXMobXlkYXRhKQpzcGFjIDwtIGxldHMuY29ycmVsKHg9bWF0eCwgeT1kaXN0bSwgej0xMiwgZXF1aWRpc3RhbnQgPSBULCBwbG90ID0gVCkKbW9yYW4udGVzdChyZXNpZHVhbHMobSksIG5iMmxpc3R3KG5iKSkkcC52YWx1ZSAjIG5vIGV2aWRlbmNlIG9mIHNwYXRpYWwgY29tcG9uZW50Li4gCgojIHNwYXRpYWwgZmlsdGVyaW5nIC0tLS0tLS0tLS0tLS0tLS0tLS0tCnJtKHNhcmNvbCkKc2FyY29sIDwtIFNwYXRpYWxGaWx0ZXJpbmcoc2NhbGUoSSh2bG9nKGNvc3QpKSkgfiBzY2FsZShJKCh0YXNfYnJlYWR0aCkpKSArIHNjYWxlKEkoKHRhc19wb3NpdGlvbikpKSArIHNjYWxlKEkoKHBjcF9icmVhZHRoKSkpICsgc2NhbGUoSSgocGNwX3Bvc2l0aW9uKSkpICsgCiAgICAgICAgICAgc2NhbGUoSSgobXRuX21hc3MpKSkgKyBzY2FsZShJKChkaXNwZXJzYWxfYWJpbGl0eSkpKSArIHNjYWxlKEkoKHBhaXJfYWdlKSkpICsgc2NhbGUoSSgoZGlzdGFuY2UpKSkgKyBzY2FsZShJKChib3VuZGFyeV9sZW5ndGgpKSkgKyAKICAgICAgICAgICBzY2FsZShJKChNQVRfb3ZlcmxhcCkpKSArIGxhbmRnYXAgKyByZWFsbSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgZGF0YSA9IG15ZGF0YSxuYj1uYiwgc3R5bGU9IlciLCBFeGFjdEVWID0gVFJVRSkKbXlkYXRhWyxjKChiYXNlY29scysxKTooYmFzZWNvbHMrMSsgZGltKGZpdHRlZChzYXJjb2wpKVsyXS0xKSldPC1maXR0ZWQoc2FyY29sKQpjb2xuYW1lcyhteWRhdGEpIAoKIyB2ZWN0b3IgMSBhZGRlZCAtLS0KbXlkYXRhJHJlYWxtMiA8LSBhcy5mYWN0b3IobXlkYXRhJHJlYWxtMik7IG15ZGF0YSRyZWFsbTIgPC0gcmVsZXZlbChteWRhdGEkcmVhbG0yLCByZWYgPSAiTlQiKQptIDwtIGdscyhzY2FsZShJKHZsb2coY29zdCkpKSB+IHNjYWxlKEkoKHRhc19icmVhZHRoKSkpICsgc2NhbGUoSSgodGFzX3Bvc2l0aW9uKSkpICsgc2NhbGUoSSgocGNwX2JyZWFkdGgpKSkgKyBzY2FsZShJKChwY3BfcG9zaXRpb24pKSkgKyAKICAgICAgICAgICBzY2FsZShJKChtdG5fbWFzcykpKSArIHNjYWxlKEkoKGRpc3BlcnNhbF9hYmlsaXR5KSkpICsgc2NhbGUoSSgocGFpcl9hZ2UpKSkgKyBzY2FsZShJKChkaXN0YW5jZSkpKSArIHNjYWxlKEkoKGJvdW5kYXJ5X2xlbmd0aCkpKSArIAogICAgICAgICAgIHNjYWxlKEkoKE1BVF9vdmVybGFwKSkpICsgbGFuZGdhcCArIHJlYWxtICsgVjIzICsgVjI0ICsgVjI1ICsgVjI2ICsgVjI3ICsgVjI4LCBjb3JyZWxhdGlvbiA9IGNvclBhZ2VsKDAuOTksIHBoeSA9IHRyZWUsIGZpeGVkID0gRiwgZm9ybSA9IH5TcGVjaWVzLjEpLCBkYXRhID0gbXlkYXRhLCBtZXRob2QgPSAiUkVNTCIpOyBzdW1tYXJ5KG0pOyBjb3IocHJlZGljdChtKSxzY2FsZShJKHZsb2cobXlkYXRhJGNvc3QpKSkpCm1hdHggPC0gYXMubWF0cml4KG0kcmVzaWR1YWxzKTsgcm93bmFtZXMobWF0eCkgPC0gcm93bmFtZXMobXlkYXRhKQpzcGFjIDwtIGxldHMuY29ycmVsKHg9bWF0eCwgeT1kaXN0bSwgej0xMiwgZXF1aWRpc3RhbnQgPSBULCBwbG90ID0gVCkKbW9yYW4udGVzdChyZXNpZHVhbHMobSksIG5iMmxpc3R3KG5iKSkkcC52YWx1ZSAjIHRvbyBtdWNoLgpoaXN0KHJlc2lkKG0pKQpwbG90KG0sIHJlc2lkKC4sIHR5cGUgPSAicCIpIH4gZml0dGVkKC4pLCBhYmxpbmUgPSAwKQpwbG90KG0sIHNjYWxlKEkodmxvZyhjb3N0KSkpIH4gZml0dGVkKC4pLCBhYmxpbmUgPSBjKDAsMSkpCnFxbm9ybShtKQoKCmZvcihpIGluIGMoIlYyMyIsICJWMjQiLCAiVjI1IiwgIlYyNiIsICJWMjciLCAiVjI4IikpewogIHggPC0gZ2dwbG90KHdvcmxkKSsKICBnZW9tX3NmKCkgKwogIGdlb21fcG9pbnQoZGF0YSA9IG15ZGF0YVtvcmRlcihteWRhdGFbLGldKSxdLCBhZXMoeT1sYXQsIHg9bG9uLCBjb2xvciA9IG15ZGF0YVssaV0pLCBhbHBoYSA9IDAuOSkgKwogIHNjYWxlX2NvbG9yX3ZpcmlkaXMoKQogIHByaW50KHgpCn0KYGBgCgoKU2Vuc2l0aXZpdHkgQW5hbHlzZXMKYGBge3J9CiMgMSBQYWlyIGFnZSAoYWxsIHYuIDwgOG15YSAoZW5kIG9mIHVwbGlmdCBvZiBBbmRlcykpCiMgMiBEaXN0YW5jZSAoYWxsIHYuIDwgMTUwMCoxMDAwKSAoMTUwMCAvIDExMCA9IH4gMjIgZGVncmVlcykKIyAzIE1BVF9vdmVybGFwICg+IDAlIHYuID4gNzUlIChtb3JlIHJlc3RyaWN0aXZlID09IG1vcmUgY29uc2VydmF0aXZlIGZvciB0aGlzIG1lYXN1cmUuKSkKIyA0IGxhbmRnYXAgKGFsbCB2LiBub2dhcCkgKkFMTCBHQVBTIEFSRSA8IDExMGttICh0d28gd2F0ZXIgZ3JpZCBjZWxscyBtYXJrZWQgYXMgbGFuZCBmb3IgaGF2aW5nID41MCUgbGFuZCBAIDAuNSBkZWdyZWUgcmVzb2x1dGlvbi4pCmBgYA==